Почему `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 в самом начале объявления.

Примечание: пример получен из этой статьи (но я немного изменил его).