Typedef и контейнеры константных указателей

Следующая строка кода компилируется просто отлично и ведет себя:

list<const int *> int_pointers;  // (1)

В следующих двух строках нет:

typedef int * IntPtr;
list<const IntPtr> int_pointers;  // (2)

Я получаю точно такие же ошибки компиляции для

list<int * const> int_pointers;  // (3)

Мне хорошо известно, что последняя строка не является законной, так как элементы контейнера STL должны быть назначаемыми. Почему интерпретация компилятора (2) совпадает с (3)?

Ответ 1

Короткий ответ:

  • - список указателей на константные int.
  • - это список постоянных указателей на ints.
  • совпадает с 2.

const (и volatile) должен естественным образом появляться после того, как они квалифицируются. Когда вы пишете это раньше, компилятор автоматически перезаписывает его внутри:

const int *

становится

int const *

который является указателем на константу int. Списки из них будут компилироваться отлично, поскольку сам указатель по-прежнему можно назначить.

Ответ 2

Вы читаете декларации типа C справа налево. Таким образом, "const int *" является указателем на константу ints ( "const int" и "int const" означает одно и то же). Это прекрасно назначается. Но (2) и (3) являются постоянными указателями на int и поэтому не присваиваются.

Ответ 3

const IntPtr и const int* - это не одно и то же.

1) const int* является "указателем на const int".

2) const IntPtr расширяется до int * const (думаю (int *) const), который является "const указателем на int".

Короче говоря, typedef действует как набор круглых скобок. Вы не можете изменить const -ness того, на что указывает указатель typedef 'd.

Ответ 4

Вы спрашиваете: "Почему интерпретация компилятора (2) совпадает с (3)?". Ну, потому что на языке С++ (а также в C) они семантически одинаковы. Когда вы определяете имя типа

typedef int *IntPtr;

то позже тип const IntPtr будет стоять за int *const, а не для const int *. Это то, как typedef-имена работают на С++.

Typedef-имена в С++ не являются макросами. Хотя они не определяют новые типы (просто псевдонимы для существующих), полученные псевдонимы все же "атомные", "монолитные", в том смысле, что любые квалификаторы, применяемые к псевдониму, будут применяться в качестве квалификаторов верхнего уровня. Когда вы работаете с typedef-name, нет никакого способа "прокрасться" в спецификатор const, чтобы он каким-то образом "спустился" к более низкоуровневой части типа (int в вашем случае).

Если вы настаиваете на использовании typedef-имен, у вас нет другого непосредственного выбора, кроме как предоставить два разных имени typedef, например

typedef int *IntPtr;
typedef const int *ConstIntPtr;

и используйте ConstIntPtr, когда вам нужна версия типа указателя на константу.

Ответ 5

const int * - это то же самое, что и запись int const *, что означает постоянное значение, указанное указателем не константы.

С помощью typedef вы определяете сам указатель как постоянный, как в своем третьем выражении.

Ответ 6

Я уверен, вы знаете, что const IntPtr и IntPtr const - это один и тот же тип. Это означает, что list<const IntPtr> и list<IntPtr const> являются одним и тем же типом. Это означает, что вы пытаетесь скомпилировать это:

typedef int * IntPtr;
         list<IntPtr const> int_pointers;  // (2bis)