Ответ 1
Более длинный комментарий как вики сообщества.
Выражение xs[0]
определено в [expr.sub]/1 как *((xs)+(0))
. (См. Ниже скобки).
Одно из выражений должно иметь тип "указатель на
T
", а другой должен иметь незапечатанную нумерацию или интегральный тип.
Следовательно, применяется преобразование от массива к указателю [conv.array]:
Значение lvalue или rvalue типа "array of
N T
" или "массив неизвестной границыT
" может быть преобразовано в prvalue типа "указатель наT
". Результатом является указатель на первый элемент массива.
Обратите внимание, что он может работать с lvalue и результатом является prvalue, 0
как целочисленный литерал также является значением prvalue. Добавление определено в [expr.add]/5. Поскольку оба являются prvalues, преобразование lvalue-rvalue не требуется.
int arr[3];
constexpr int* p = arr; // allowed and compiles
Ключевым шагом теперь является косвенность *
[expr.unary.op]/1
Унарный оператор
*
выполняет косвенное обращение: выражение, к которому оно применяется, должно быть указателем на тип объекта или указателем на тип функции, а результатом является значение l, относящееся к объекту или функции, выражения.
Итак, результат xs[0]
- это lvalue, относящийся к первому элементу массива xs
, и имеет тип int const
.
N.B. [Expr.prim.general]/6
Выражение в скобках - это основное выражение, тип и значение которого идентичны типу заключенного выражения. Наличие скобок не влияет на то, является ли выражение lvalue.
Если теперь посмотреть на маркеры в [expr.const]/2, которые запрещают отображение определенных выражений и преобразований в постоянных выражениях, единственной применимой маркой (AFAIK) является преобразование lvalue-to-rvalue:
преобразование lvalue-to-rvalue (4.1), если оно не применяется к
нестабильный glvalue интегрального или перечисляемого типа, который ссылается на энергонезависимый объект const с предшествующей инициализацией, инициализированный константным выражением [Примечание: строковый литерал (2.14.5) соответствует массиву таких объектов. -end note] или
нестабильный glvalue типа literal, который ссылается на энергонезависимый объект, определенный с помощью
constexpr
, или который ссылается на под-объект такого объекта, или[...]
Но единственное истинное преобразование lvalue-to-rvalue в соответствии с (4.1) (не 4.2, которое представляет собой массив-указатель), которое появляется при оценке xs[0]
, - это преобразование из полученной lvalue, относящейся к первому элемент.
Для примера в OP:
int const xs[]{1, 2, 3};
int as[xs[0]]; // error.
Этот элемент xs[0]
имеет нелетучий const-интегральный тип, его инициализация предшествует выражению константы там, где оно встречается, и оно было инициализировано постоянным выражением.
Кстати, добавленная "Заметка" в цитированном отрывке [expr.const]/2 была добавлена, чтобы уточнить, что это законно:
constexpr char c = "hello"[0];
Обратите внимание, что строковый литерал также является lvalue.
Было бы здорово, если бы кто-то (может изменить это) объяснил, почему xs[0]
не может появляться в постоянном выражении.