Подтвердить что ты не робот

Почему элемент разметки в const-векторе int-указателей изменен?

Я не уверен в истинном значении const vector<int *>, поэтому я скомпилировал приведенный ниже код, чтобы получить представление, но теперь я больше смущен.

vector<int *> v;
int x = 1, y = 2;
v.push_back(&x);
v.push_back(&y);

const vector<int *> w = v;
w[0] = &y;   //failed. Element is a constant pointer?
*(w[0]) ++;  //failed. Element pointer references to constant value?

Если бы я остановился здесь, я бы предположил, что const vector<int *> является вектором const int * const, но затем я попробовал следующее, что явно противоречило этому предположению.

*(w[0]) += 3; //passed. Value not constant?
*(w[0]) = 20; //passed. Why...

Теперь *(w[0]) по неизвестной мне причине явно относится к ++ и += и присваиванию по-разному. Я убедил себя, что const vector объявляет только постоянный объект класса vector и что приведенные выше результаты могут зависеть от фактической реализации перегрузки оператора класса vector. Но я не могу обмотать голову вокруг этого. Может кто-нибудь объяснить, пожалуйста?

Если это актуально, я использовал g++ 4.2 на Mac.

4b9b3361

Ответ 1

Почему элемент разметки в const-векторе int-указателей изменен?

Для const vector<int *> элемент будет const указателем на const, т.е. int * const, поэтому вы можете изменить объект, указанный указателем, но не сам указатель.

Согласно Приоритет оператора, оператор приращения postfix имеет более высокий приоритет, чем operator*, поэтому *(w[0]) ++; эквивалентно

* ((w[0]) ++);

Приращение указателя выполняется сначала, а затем оно терпит неудачу. w[0] = &y; также пытается изменить указатель, поэтому он тоже не работает.

С другой стороны, (*w[0]) ++; (т.е. приращение на pointee) будет в порядке. И следующие утверждения тоже хороши, потому что они оба изменяют объекты, на которые указывает указатель, а не указатели.

*(w[0]) += 3; //passed.
*(w[0]) = 20; //passed.

Ответ 2

Это вопрос приоритет оператора.

Когда вы выполняете *(w[0]) ++, вы пытаетесь изменить указатель.

Когда вы выполняете *(w[0]) += 3, вы изменяете данные, на которые указывает указатель.

Ответ 3

w является const vector<int *>. К вектору применяется спецификатор const. Поэтому соответствующая функция-член const будет использоваться для operator[]:

const_reference operator[]( size_type pos ) const;

Поскольку вектор const -qualified и содержит элементы типа int * (а не const int *), тип выражения w[0] равен int * const& (вместо const int *&). То есть, это ссылка на константный указатель на int, а не на ссылку на указатель на константу int: константа применяется к самому указателю, а не к указанным данным.

Выполняя *(w[0]) += 3, вы не изменяете значение указателя, возвращаемого вектором (которое равно const), но значение, на которое указывает этот указатель. Поскольку этот указатель имеет тип int * const (а не const int *), вы можете изменить то, на что он указывает, поэтому он работает. Однако выполнение w[0] = &y выполняет присвоение указателю константы, поэтому он не компилируется.

Ответ 4

const vector<T> позволяет получить доступ к своим элементам как T const & (т.е. const T &). В этом случае T равен int *, поэтому это int * const &, ссылка const на указатель, указывающий на int. Указатель является константой, но int не является.

Тип вектора должен был бы быть vector<int const *> (т.е. vector<const int*>), и в этом случае к элементам можно было бы получить доступ через int const * const &.

Нижняя строка, константа транзитивна с шаблонами, но не с указателями. И если вы помещаете указатели в шаблоны, вы получаете немного оба поведения.