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)?
6 ответов
Короткий ответ:
- список указателей на постоянные числа
- список константных указателей на целые
- такой же, как 2.
const (и volatile) должны естественно появляться после того типа, который они квалифицируют. Когда вы пишете это раньше, компилятор автоматически переписывает это внутренне:
const int *
становится
int const *
который является указателем на константу int. Их списки будут компилироваться нормально, так как сам указатель все еще может быть назначен.
Вы читаете объявления в стиле C справа налево. Таким образом, "const int *" является указателем на постоянные целые числа ("const int" и "int const" означают одно и то же). Это прекрасно назначаемо. Но (2) и (3) являются постоянными указателями на int, и, следовательно, не присваиваются.
Вы спрашиваете "Почему компилятор интерпретирует (2) так же, как (3)?". Ну, потому что в языке C++ (как и в C) они семантически одинаковы. Когда вы определяете имя типа как
typedef int *IntPtr;
потом тип const IntPtr
будет стоять за int *const
, не для const int *
, Именно так работают имена типов в C++.
Имена типов в C++ не являются макросами. Хотя они не определяют новые типы (только псевдонимы для существующих), полученные псевдонимы, тем не менее, являются "атомарными", "монолитными" в том смысле, что любые квалификаторы, примененные к псевдониму, будут применяться как квалификаторы верхнего уровня. Когда вы работаете с typedef-name, нет способа "проникнуть" в спецификатор const, чтобы он каким-то образом "спустился" в часть типа более низкого уровня (int
в твоем случае).
Если вы настаиваете на использовании typedef-имён, у вас нет другого немедленного выбора, кроме как предоставить два разных typedef-имени, например
typedef int *IntPtr;
typedef const int *ConstIntPtr;
и использовать ConstIntPtr
когда вам нужна указатель на константную версию типа.
const IntPtr
а также const int*
это не одно и то же.
1) const int*
это "указатель на const int
".
2) const IntPtr
расширяется до int * const
(считать (int *) const
) который "const
указатель на int
".
Короче говоря, typedef
действует как набор скобок. Вы не можете изменить const
-Что из того, что typedef
указатель указывает на.
const int *
так же, как писать int const *
, что означает постоянное значение, на которое указывает непостоянный указатель.
С помощью typedef вы сами определяете указатель как константу, как в третьем выражении.
Я уверен, что вы знаете, что const IntPtr
а также IntPtr const
того же типа. Что значит list<const IntPtr>
а также list<IntPtr const>
того же типа. Что означает, что вы пытаетесь скомпилировать это:
typedef int * IntPtr;
list<IntPtr const> int_pointers; // (2bis)