неизменяемый вектор / массив не переназначаемых, но изменяемых членов? - PullRequest
2 голосов
/ 02 июля 2010

Есть ли способ сделать неизменяемый вектор / массив из не переназначаемых, но изменяемых элементов? Самое близкое, что я могу себе представить, - это использовать vector<T *> const копию, созданную из временного файла, но поскольку при инициализации я знаю, сколько именно и чего я хочу, я бы предпочел иметь блок объектов, а не указатели. Возможно ли что-то похожее на то, что показано ниже, с std::vector или каким-то еще неясным ускорением и т. Д. С шаблоном?

// Struct making vec<A> that cannot be resized or have contents reassigned.
struct B {
  vector<A> va_; // <-- unknown modifiers or different template needed here
  vector<A> va2_;

  // All vector contents initialized on construction.
  Foo(size_t n_foo) : va_(n_foo), va2_(5) { }

  // Things I'd like allowed: altering contents, const_iterator and read access.
  good_actions(size_t idx, int val) {
    va_[idx].set(val);

    cout << "vector<A> info - " <<  " size: " << va_.size() << ", max: "
      << va_.max_size() << ", capacity: " << va_.capacity() << ", empty?: "
      << va_.empty() << endl;

    if (!va_.empty()) {
      cout << "First (old): " << va_[0].get() << ", resetting ..." << endl;
      va_[0].set(0);
    }

    int max = 0;
    for (vector<A>::const_iterator i = va_.begin(); i != va_.end(); ++i) {
      int n = i->get();
      if (n > max) { max = n; }
      if (n < 0)   { i->set(0); }
    }
    cout << "Max : " << max << "." << endl;
  }

  // Everything here should fail at compile.
  bad_actions(size_t idx, int val) {
    va_[0]    = va2_[0];
    va_.at(1) = va2_.at(3);

    va_.swap(va2_);
    va_.erase(va_.begin());
    va_.insert(va_.end(), va2_[0]);

    va_.resize(1);
    va_.clear();
    // also: assign, reserve, push, pop, .. 
  }
};

Ответы [ 4 ]

2 голосов
/ 02 июля 2010

Существует проблема с вашими требованиями.Но сначала давайте рассмотрим проблему фиксированного размера, она называется std::tr1::array<class T, size_t N> (если вы знаете размер во время компиляции).

Если вы не знаете его во время компиляции, вы все равно можете использовать некоторый прокси-класс поверхвектор.

template <class T>
class MyVector
{
public:
  explicit MyVector(size_t const n, T const& t = T()): mVector(n,t) {}

  // Declare the methods you want here
  // and just forward to mVector most of the time ;)

private:
  std::vector<T> mVector;
};

Однако какой смысл не назначаться, если вы изменчивы?Ничто не мешает пользователю выполнять тяжелую работу:

class Type
{
public:
  int a() const { return a; }
  void a(int i) { a = i; }

  int b() const { return b; }
  void b(int i) { b = i; }
private:
  Type& operator=(Type const&);

  int a, b;
};

Ничто не мешает мне делать:

void assign(Type& lhs, Type const& rhs)
{
  lhs.a(rhs.a());
  lhs.b(rhs.b());
}

Я просто хочу ударить тебя по голове за усложнение моей жизни...

Возможно, не могли бы вы более точно описать, что вы хотите сделать, хотите ли вы ограничить подмножество возможных операций в вашем классе (некоторые переменные не должны быть доступны для изменения, а другие могут)?

В этом случае вы можете снова использовать прокси-класс

class Proxy
{
public:
  // WARN: syntax is screwed, but `vector` requires a model
  // of the Assignable concept so this operation NEED be defined...
  Proxy& operator=(Proxy const& rhs)
  {
    mType.a = rhs.mType.a;
    // mType.b is unchanged
    return *this;
  }

  int a() const { return mType.a(); }
  void a(int i) { mType.a(i); }      

  int b() const { return mType.b(); }

private:
  Type mType;
};

Не так много вы не можете сделать с подходящими прокси.Это, пожалуй, самый полезный шаблон, который я когда-либо видел.

1 голос
/ 02 июля 2010

То, что вы спрашиваете, на самом деле не возможно.

Способ only для предотвращения назначения чего-либо - это определить operator = для этого типа как private. (Как расширение этого, поскольку методы const operator = не имеют особого смысла (и, следовательно, являются редкостью), вы можете приблизиться к этому, только разрешив доступ к ссылкам на const из вашего контейнера. Но пользователь все еще может определить const operator =, и вам все равно нужны изменяемые объекты.)

Если вы думаете об этом, std::vector::operator [] возвращает ссылку на значение, которое оно содержит. Использование оператора присваивания вызовет operator = для значения. std::vector здесь полностью игнорируется (за исключением вызова operator[], используемого для получения ссылки в первую очередь), поэтому у него (std::vector) нет возможности каким-либо образом переопределить вызов operator = функция.

Все, что вы делаете для прямого доступа к членам объекта в контейнере, должно будет возвращать ссылку на объект, которая затем может использоваться для вызова operator = объекта. Таким образом, контейнер никак не может помешать назначению объектов внутри него, если только контейнер не реализует прокси для содержащихся в нем объектов, у которого есть закрытый оператор присваивания, который ничего не делает и перенаправляет другие вызовы «реальному» объекту, но делает не разрешать прямой доступ к реальному объекту (хотя, если это имеет смысл, вы можете вернуть копии реального объекта).

1 голос
/ 02 июля 2010

Не могли бы вы создать класс, который содержит ссылку на ваш объект, но его конструкторы доступны только его другу std :: vector?

Например:

template<typename T>
class MyRef {
   firend class std::vector< MyRef<T> >
public:
   T& operator->();
[...etc...]
0 голосов
/ 02 июля 2010

Вы можете достичь того, что вы хотите, сделав std::vector const, а вектор * struct или class data mutable.Ваш set метод должен быть const.Вот пример, который работает, как и ожидалось, с g ++:

#include <vector>

class foo
{ 
public:
  foo () : n_ () {}
  void set(int n) const { n_ = n; }

private:

  mutable int n_;
};

int main()
{
  std::vector<foo> const a(3);  // Notice the "const".
  std::vector<foo> b(1);

  // Executes!
  a[0].set(1);

  // Failes to compile!
  a.swap(b);
}

Таким образом, вы не можете каким-либо образом изменить vector, но вы можете изменить mutable члены-данные объектов, содержащихся в vector.Вот как этот пример компилируется:

g++ foo.cpp 
foo.cpp: In function 'int main()':
foo.cpp:24: error: passing 'const std::vector<foo, std::allocator<foo> >' as 'this' argument of 'void std::vector<_Tp, _Alloc>::swap(std::vector<_Tp, _Alloc>&) [with _Tp = foo, _Alloc = std::allocator<foo>]' discards qualifiers

Единственный недостаток, о котором я могу подумать, заключается в том, что вам нужно больше знать о const -корректности вашего кода, ноэто также не обязательно является недостатком.

HTH!

РЕДАКТИРОВАТЬ / Уточнение : Цель этого подхода - не полностью победить const.Скорее цель состоит в том, чтобы продемонстрировать средства достижения требований, изложенных в вопросе OP, с использованием стандарта C ++ и STL.Это не идеальное решение, поскольку оно предоставляет метод const, который позволяет изменять внутреннее состояние, видимое для пользователя.Конечно, это проблема с этим подходом.

...