Может ли строковый литерал быть индексированным в постоянном выражении?

Это справедливо, поскольку выражению 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.

Ответ 1

Цель состоит в том, что это работает и абзацы, указывающие, что значение lvalue для rvalue действительно, будет изменяться с примечанием, в котором говорится, что lvalue, который ссылается на подобъект строкового литерала, представляет собой объект с целым числом, инициализированный константным выражением (которое описывается как один из разрешенных случаев) в проекте post-С++ 11.

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

Ответ 2

Что касается вашего вопроса о #if, это не было целью комитета по стандартизации увеличить набор выражений, которые могут использоваться в препроцессоре, а текущая формулировка считается дефектом. Это будет указано в качестве основной проблемы 1436 в почтовой рассылке post-Kona WG21. Спасибо, что привлекли наше внимание!