typedef и контейнеры константных указателей - PullRequest
8 голосов
/ 27 января 2010

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

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 ]

9 голосов
/ 27 января 2010

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

  1. - список указателей на постоянные целые.
  2. - список константных указателей на целые числа.
  3. равно 2.

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

const int *

становится

int const *

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

8 голосов
/ 27 января 2010

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

6 голосов
/ 27 января 2010

Вы спрашиваете «Почему компилятор интерпретирует (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, когда вам нужна версия типа указатель на констант.

5 голосов
/ 27 января 2010

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

1) const int* - это "указатель на const int".

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

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

3 голосов
/ 27 января 2010

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

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

0 голосов
/ 10 ноября 2010

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

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