Почему постоянный указатель не может быть постоянным?
Следующая программа компилирует:
template <const int * P>
class Test{};
extern const int var = 42; //extern needed to force external linkage
int main()
{
Test<&var> test;
}
Этот, однако, не делает, что для меня неожиданно:
template <const int * P>
class Test{};
extern const int var = 42; //extern needed to force external linkage
extern const int * const ptr = &var; //extern needed to force external linkage
int main()
{
Test<ptr> test; //FAIL! Expected constant expression.
}
Альтернативный пример:
int main()
{
const int size = 42;
int ok[*&size]; //OK
const int * const pSize = &size;
int fail[*pSize]; //FAIL
}
Я пришел к выводу, что указатель просто не может быть константным выражением, независимо от того, const или инициализирован константным выражением.
Вопросы:
- Верно ли мое заключение?
- Если да, то почему указатель не может быть постоянным выражением? Если нет, то почему эти программы не компилируются?
- Может ли С++ 0x (С++ 11) изменить что-нибудь?
Спасибо за любые идеи!
Ответы
Ответ 1
Это немного сложнее. В С++ 03 и С++ 11 &var
является константным выражением, если var
является локальной статической/статической переменной класса или областью имен. Это называется адресным константным выражением. Инициализация статической переменной класса или переменной указателя области имен с этим константным выражением гарантированно будет выполнена до запуска любого кода (фаза статической инициализации), поскольку она является константным выражением.
Однако только с С++ 11 переменная-указатель constexpr, которая хранит адрес &var
также может использоваться как выражение константы адреса, и только с С++ 11 вы можете разыменовать выражение константы адреса (на самом деле, вы можете разыменовать еще больше - даже адреса локальных элементов массива, но давайте оставим их на месте), и если они ссылаются на постоянную целочисленную переменную, инициализированную до разыменования, или на переменную constexpr, вы снова получаете постоянное выражение (в зависимости от типа и категории значения, вида константы). выражение может отличаться). Таким образом, следующее действительно С++ 11:
int const x = 42;
constexpr int const *px = &x;
// both the value of "px" and the value of "*px" are prvalue constant expressions
int array[*px];
int main() { return sizeof(array); }
Если так, почему указатель не может быть константным выражением? Если нет, то почему вышеупомянутые программы не компилируются?
Это известное ограничение в стандартной редакции - в настоящее время он допускает только другие параметры шаблона в качестве аргументов или & object
для параметра шаблона типа указателя. Хотя компилятор должен быть способен делать гораздо больше.
Ответ 2
Он все еще не разрешен в С++ 0x. temp.arg.nontype
требует:
Аргумент шаблона для непигового шаблона-шаблона без шаблона должен быть одним из следующих:
- для нетипового шаблона-параметра интегрального или перечисляемого типа, преобразованного константного выражения (5.19) типа шаблона-параметра; или
- имя несимметричного шаблона; или
- константное выражение (5.19), которое обозначает адрес объекта со статическим временем хранения и внешняя или внутренняя связь или функция с внешней или внутренней связью, включая функциональные шаблоны и функции-шаблоны функций, но исключая нестатические члены класса, выраженные (игнорируя круглые скобки), как
&
id-expression
, за исключением того, что и может быть опущено, если имя относится к функции или массиву и должно опускаться, если соответствующий шаблон-параметр является ссылкой; или - константное выражение, которое вычисляет значение нулевого указателя (4.10); или
- константное выражение, которое вычисляет значение указателя нулевого элемента (4.11); или
- указатель на элемент, выраженный как описано в 5.3.1.
оригинальный ответ:
- В С++ 03 интегральные выражения могут быть постоянными выражениями.
- Потому что стандарт говорит так (естественно).
- В С++ 0x, n3290 включает примеры с использованием
constexpr
на указателе. Итак, теперь вы можете попробовать, хотя теперь вы должны использовать ключевое слово constexpr
вместо верхнего уровня const
.
Также присутствует ошибка gcc, g++ отклоняет стандартные черновики собственных примеров допустимого использования constexpr
.
Ответ 3
Проблема заключается в том, что ваша программа на С++ может быть загружена в любой точке памяти, поэтому адрес глобального var
может быть различным при каждом запуске программы. Что произойдет, если вы дважды запустите свою программу? var
, очевидно, находится в двух разных местах.
Хуже того, в вашем примере вы берете адрес переменной в стеке! посмотрите на это:
void myfunction( unsigned int depth) {
const int myvar = depth;
const int * const myptr = &myvar;
if (depth)
myfunction(depth-1);
}
Если основные вызовы myfunction (3), то 3 myvars создаются в разных местах. Нет никакого способа, чтобы время компиляции даже знало, сколько создано myvars, тем более точное местоположение.
Наконец: объявление переменной const
означает: "Я обещаю" и не означает, что это постоянная времени компиляции. См. Этот пример:
int main(int argc, char** argv) {
const int cargc = argc;
char* myargs[cargc]; //the size is constant, but not a _compile time_ constant.
}