Почему `const` работает над тем, что непосредственно предшествует этому?
Я изучаю С++. В моем курсе объясняется, что лучше всего разместить const
сразу после того, что вы хотите сделать неизменным, потому что это работает const
.
Многие люди, в том числе, например, сам Бьярн Страуструп, любят писать const
впереди. Но это иногда приводит к проблемам:
const int *foo; //modifiable pointer to a constant int.
int const *bar; //constant pointer to a modifiable int? No! It a modifiable pointer to a constant int. (So the same type as foo)
Пример, который показывает это в действии:
int fun(int const *mypointer)
{
*mypointer = 5; //Won't compile, because constant int.
mypointer = 0; // Is okay, because modifiable pointer.
}
Что еще более сбивает с толку, так это то, что компиляторы, такие как g++, переписывают int const bar
в const int bar
в свои сообщения об ошибках.
Теперь это поведение меня очень сбивает с толку. Почему const
работает таким образом? Казалось бы, гораздо легче понять, будет ли это "работать" над тем, что было после него.
Ответы
Ответ 1
С++ следует за синтаксисом C.
Это выглядит странно, но в C вы указываете не тип переменной, но тип выражения с ним:
-
int v
означает, что v
есть int
;
-
int *v
означает, что *v
есть int
, поэтому v
является указателем на int
;
-
int v[]
означает, что v[…]
является int
, поэтому v
является массивом int
;
-
int v()
означает, что v()
int
, поэтому v
возвращает функцию int
;
- и т.д. (вам всегда нужно прочитать такое выражение из внутреннего).
Более подробно с вашим вопросом, в спецификации типа C может состоять из нескольких слов: unsigned char
, long int
, const double
(даже больше двух - const unsigned long long int
). Количество повторяющихся слов имеет значение (long long int
в общем случае отличается от long int
), но порядок слов не равен (long int
совпадает с int long
). Поэтому const int *p
совпадает с int const *p
(а также const int i
совпадает с int const i
).
Что касается int * const p
, он, вероятно, не подчиняется общей схеме. Поскольку в C нет такого выражения, как * const p
(где p
- переменная), поэтому мы не можем его объяснить чем-то вроде "выражение * const p
будет иметь тип int
". Однако подумайте, где еще мы можем поместить ключевое слово const
, чтобы указать, что сам указатель является постоянным, а не его разыменованным значением? (Предполагая, что как const int *p
, так и int const *p
означают, что разыменованное значение является постоянным, и мы не хотим вводить дополнительные слова в язык.) Нигде, кроме *
; так что вот оно.
Ответ 2
Чтобы понять декларацию C, существует правое правое правило, которое очень полезно: например
int *
является указателем на целое число (обратите внимание, что вы должны прочитать это справа налево). То же самое для ссылки на int:
int &
Теперь прочитайте тип p8 справа налево:
char * const * const p8; // const pointer to const pointer to char
Я подозреваю, что const изменяет предыдущий тип, просто чтобы соответствовать этому правилу. Исключение составляет тот факт, что вы можете поместить const в самом начале объявления.
Примечание: пример получен из этой статьи (но я немного изменил его).