Почему элемент разметки в 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.
Ответы
Ответ 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 &
.
Нижняя строка, константа транзитивна с шаблонами, но не с указателями. И если вы помещаете указатели в шаблоны, вы получаете немного оба поведения.