Значение указателя Constexpr

Я пытаюсь объявить указатель constexpr, инициализированный некоторым постоянным целочисленным значением, но clang уничтожает все мои попытки:

Попытка 1:

constexpr int* x = reinterpret_cast<int*>(0xFF);

test.cpp:1:20: note: reinterpret_cast is not allowed in a constant expression

Попытка 2:

constexpr int* x = (int*)0xFF;

test.cpp:1:20: note: cast which performs the conversions of a reinterpret_cast is not allowed in a constant expression

Попытка 3:

constexpr int* x = (int*)0 + 0xFF;

test.cpp:1:28: note: cannot perform pointer arithmetic on null pointer

Я пытаюсь не разрешать дизайн? Если да, то почему? Если нет, как я могу это сделать?

Примечание: gcc принимает все эти данные.

Ответ 1

Как отмечает Люк Дантон, ваши попытки блокируются правилами в [expr.const]/2, которые говорят, что в выражении core constant не допускаются различные выражения, включая:

- a reinterpret_cast
- операция, которая имела бы поведение undefined [Примечание: включая [...] определенную арифметику указателя [...] - примечание конца]

Первая пуля исключает ваш первый пример. Второй пример исключается первой маркой выше, плюс правило из [expr.cast]/4, что:

Преобразования, выполненные с помощью [...] a reinterpret_cast [...], могут быть выполнены с использованием литой нотации преобразования явного типа. Используются те же смысловые ограничения и поведение.

Вторая пуля была добавлена ​​основной проблемой 1321 в WG21 и поясняет, что арифметика указателя на нулевом указателе недопустима в постоянном выражении. Это исключает ваш третий пример.

Даже если эти ограничения не применимы к основным константным выражениям, было бы невозможно инициализировать указатель constexpr со значением, полученным путем литья целого числа, поскольку переменная-указатель constexpr должна быть инициализирована символом адресное константное выражение, которое посредством [expr.const]/3 должно оцениваться как

адрес объекта со статической продолжительностью хранения, адресом функции или значением нулевого указателя.

Целое число, отличное от типа указателя, не является ничем.

g++ еще не строго соблюдает эти правила, но его последние выпуски приближаются к ним, поэтому мы должны предположить, что он в конечном итоге полностью реализует их.

Если ваша цель - объявить переменную, для которой выполняется статическая инициализация, вы можете просто отказаться от constexpr - и clang, и g++ выдадут статический инициализатор для этого выражения. Если вам нужно, чтобы это выражение было частью постоянного выражения по какой-либо причине, у вас есть два варианта:

  • Реструктурируйте свой код, чтобы intptr_t был передан вместо указателя и применил его к типу указателя, когда вам нужно (вне константного выражения) или
  • Используйте __builtin_constant_p((int*)0xFF) ? (int*)0xFF : (int*)0xFF. Эта точная форма выражения (с __builtin_constant_p в левой части условного оператора) отключает проверку строгого постоянного выражения в объятиях условного оператора и мало известна, но documented, не переносимое расширение GNU, поддерживаемое как gcc, так и clang.

Ответ 2

Причиной является тот, который задается сообщением об ошибке (за один раз, очень полезно): reinterpret_cast не допускается в постоянном выражении. Он был указан как одно из явных исключений в 5.19 (параграф 2).

Изменение reinterpret_cast в листинг C-стиля по-прежнему заканчивается семантическим эквивалентом reinterpret_cast, поэтому это не помогает (и снова сообщение очень явное).

Если у вас был способ получить указатель со значением 0, вы действительно могли бы использовать p + 0xff, но я не могу придумать способ получения такого указателя с постоянным выражением. Вы могли бы полагаться на значение нулевого указателя (0 в контексте указателя, как и вы, или nullptr), имеющем значение 0 для вашей реализации, но, как вы видели, ваша реализация отказывается это сделать. Я думаю, это позволило сделать это. (Например, реализациям разрешено вызывать большинство константных выражений.)