Это справедливо, поскольку выражению constexpr
разрешено принимать значение "glvalue типа literal, который ссылается на энергонезависимый объект, определенный с помощью constexpr, или который относится к под-объекту такого объекта" (§5.19/2):
constexpr char str[] = "hello, world";
constexpr char e = str[1];
Однако, казалось бы, строковые литералы не соответствуют этому описанию:
constexpr char e = "hello, world"[1]; // error: literal is not constexpr
2.14.5/8 описывает тип строковых литералов:
Обычные строковые литералы и строковые литералы UTF-8 также называются узкими строковыми литералами. Узкий строковый литерал имеет тип "массив n const char", где n - размер строки, как определено ниже, и имеет статическую продолжительность хранения.
Казалось бы, объект такого типа может быть проиндексирован, если бы он был временным, а не статическим временем хранения (5.19/2, сразу после вышеприведенного фрагмента):
[
constexpr
разрешает преобразование lvalue-to-rval]... значение glvalout типа literal, которое ссылается на энергонезависимый временный объект, срок жизни которого еще не закончился, инициализируется константным выражением
Это особенно странно, так как взятие lvalue временного объекта обычно "обманывает". Я полагаю, что это правило применяется к аргументам функции ссылочного типа, например, в
constexpr char get_1( char const (&str)[ 6 ] )
{ return str[ 1 ]; }
constexpr char i = get_1( { 'y', 'i', 'k', 'e', 's', '\0' } ); // OK
constexpr char e = get_1( "hello" ); // error: string literal not temporary
Для того, что стоит, GCC 4.7 принимает get_1( "hello" )
, но отклоняет "hello"[1]
, потому что "значение"._0 не используется в постоянном выражении "... но "hello"[1]
допустимо как метка case или массив оценка.
Я раскалываю некоторые стандартные волосы здесь... правильно ли анализ, и было ли какое-то намерение дизайна для этой функции?
РЕДАКТИРОВАТЬ: О... для этого есть мотивация. Кажется, что это выражение является единственным способом использования таблицы поиска в препроцессоре. Например, это вводит блок кода, который игнорируется, если SOME_INTEGER_FLAG
равно 1 или 5, и вызывает диагностику, если она больше 6:
#if "\0\1\0\0\0\1"[ SOME_INTEGER_FLAG ]
Эта конструкция будет новой для С++ 11.