Подтвердить что ты не робот

Почему постоянный указатель не может быть постоянным?

Следующая программа компилирует:

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) изменить что-нибудь?

Спасибо за любые идеи!

4b9b3361

Ответ 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.
}