Почему константный указатель не может быть константным выражением? - PullRequest
30 голосов
/ 12 сентября 2011

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

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
}

Я пришел к выводу, что указатель просто не может быть константным выражением независимо от того, является ли он константным и инициализирован константным выражением.

Вопросы:

  1. Верен ли мой вывод?
  2. Если так, почему указатель не может быть константным выражением?Если нет, то почему вышеперечисленные программы не компилируются?
  3. Изменяет ли C ++ 0x (C ++ 11, если хотите) что-нибудь?

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

Ответы [ 3 ]

12 голосов
/ 13 сентября 2011

Это немного сложнее. В C ++ 03 и C ++ 11 &var является константным выражением, если var является локальной статической / статической переменной класса или областью имен. Это называется адресным константным выражением. Инициализация статической переменной класса или переменной указателя области имен с этим константным выражением гарантированно будет выполнена до запуска любого кода (фаза статической инициализации), поскольку она является константным выражением.

Однако только с C ++ 11 переменная-указатель constexpr , в которой хранится адрес &var, также может использоваться как выражение константы адреса, и только с C ++ 11 вы можете разыменовать адрес константное выражение (на самом деле, вы можете разыменовывать еще больше - даже адреса элементов локального массива, но давайте оставим его на месте), и если оно ссылается на постоянную целочисленную переменную, инициализированную до разыменования, или на переменную constexpr, вы снова получаете константное выражение (в зависимости от в типе и категории значений тип константного выражения может быть разным). Таким образом, в C ++ 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 голосов
/ 12 сентября 2011

Это все еще не разрешено в C ++ 0x. temp.arg.nontype требуется:

Аргумент шаблона для нетипичного, не шаблонного параметра шаблона должен быть одним из:

  • для нетипового шаблона-параметра целочисленного типа или типа перечисления - преобразованное константное выражение (5.19) типа шаблона-параметра; или
  • имя нетипового шаблона-параметра; или
  • константное выражение (5.19), которое обозначает адрес объекта со статической продолжительностью хранения и внешняя или внутренняя связь или функция с внешней или внутренней связью, включая шаблоны функций и идентификаторы шаблона функции, но исключая нестатические члены класса, выражается (игнорируя скобки) как & id-expression, за исключением того, что & может быть опущено, если имя относится к функции или массиву и должно не указывать, если соответствующий шаблон-параметр является ссылкой; или
  • константное выражение, которое оценивается как нулевое значение указателя (4.10); или
  • константное выражение, которое оценивается как нулевое значение указателя на член (4.11); или
  • указатель на член, выраженный как описано в 5.3.1.

оригинальный ответ:

  1. В C ++ 03 постоянными выражениями могут быть только целочисленные выражения.
  2. Потому что стандарт говорит так (естественно).
  3. В C ++ 0x n3290 включает примеры использования constexpr в качестве указателя. Так что то, что вы пытаетесь сделать, теперь должно быть возможным, хотя теперь вы должны использовать ключевое слово constexpr вместо верхнего уровня const.

Также имеется ошибка gcc, g ++ отвергает собственные примеры стандартного черновика действительного constexpr использования .

1 голос
/ 12 сентября 2011

Проблема в том, что ваша программа на C ++ может быть загружена в любой точке памяти, и поэтому глобальный адрес var может отличаться при каждом запуске программы.Что произойдет, если вы запустите свою программу дважды?var, очевидно, находится в двух разных местах.

Еще хуже, в вашем примере вы берете адрес переменной в стеке!посмотрите на это:

void myfunction( unsigned int depth) {
     const int myvar = depth;
     const int * const myptr = &myvar;
     if (depth)
         myfunction(depth-1);
}

Если main вызывает 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.
}
...