Подтвердить что ты не робот

Разрешен ли оператор запятой в константном выражении в С++ 11?

В процессе ответа на этот вопрос на SO для С++ 11 я понял, что в С++ 03 (а также в C) использование оператор запятой явно запрещен в постоянном выражении.

В параграфе 5.19/1 стандарта С++ 03 для постоянных выражений говорится:

[...] В частности, кроме выражения sizeof, функций, объектов класса, указателей или ссылки не должны использоваться, а назначение, приращение, декремент, функциональный вызов или запятые должны не используется.

В С++ 11, однако, последняя часть, в которой упоминается оператор запятой, кажется, исчезла. И хотя в параграфе 5.19/2 Стандарта С++ 11 четко указывается, что выражения вызова функции присваивания, приращения, декремента и не constexpr не должны выступать в качестве подвыражений константного выражения, использование оператора запятой кажется, больше не запрещается.

Например, следующая программа компилируется в GCC 4.7.2 и Clang 3.3 с помощью std=c++11 (кроме предупреждений компилятора, говорящих, что оператор запятой не действует, а переменные x и arr не используются):

int main()
{
    constexpr int x = (0, 42);
    int arr[(0, 42)];
}

Однако следует сказать, что даже следующая программа компилируется с опцией -std=c++03 (как на Clang, так и на GCC), что явно неверно, учитывая приведенную выше цитату из стандарта С++ 03:

int main()
{
    int arr[(0, 42)];
}

Вопрос:

Есть ли разница между С++ 03 и С++ 11 относительно того, разрешен ли оператор запятой в постоянном выражении, или я что-то не хватает?

В качестве бонусного (неконструктивного) вопроса мне было бы интересно узнать, почему оператор запятой не может использоваться в постоянном выражении в С++ 03.

4b9b3361

Ответ 1

  1. Да, я считаю, что это изменение между C++ 03 и C++ 11. Я полагаю, что это было сделано примерно по той причине, на которую вы намекаете - что нет особенно веской причины, по которой оператор запятой не может быть частью константного выражения.

  2. Я считаю, что правило в C++ 03 возникло из правила в C (C90, §6.4):

Выражения констант не должны содержать операторы присваивания, приращения, декремента, вызова функции или запятой, кроме случаев, когда они содержатся в операнде оператора sizeof.

Относительно того, почему оператор запятой был запрещен в константных выражениях в C, я могу только догадываться. Мое непосредственное предположение должно было бы гарантировать, что определение как:

int x[5, 2];

... будет отклонено. Если бы это было разрешено, это могло бы привести программиста к ошибочному убеждению, что он определил массив элементов 5x2 (всего 10 элементов), когда (если разрешен оператор запятой) он действительно определил x только с 2 элементами (и 5 был фактически полностью проигнорирован).

Что касается того, почему комитет C++ посчитал этот риск более приемлемым, чем комитет C, я бы предположил, что это сводится к довольно простой ситуации: C почти не предоставляет альтернативы, поэтому массивы используются довольно редко. C++, с другой стороны, предоставляет как std::array, так и std::vector, оставляя крайне мало ситуаций, в которых есть много оснований для использования "сырого" массива, поэтому проблема с меньшей вероятностью возникнет.

Ответ 2

Однако следует сказать, что даже следующая программа компилируется с опцией -std = С++ 03 (как на Clang, так и на GCC), что явно неверно, учитывая приведенную выше цитату из С++ 03 Стандартный

Не так быстро. Вы также должны использовать -pedantic (или -pedantic-errors), чтобы заставить Clang и GCC строго соблюдать правила С++ 03. При этом соединительная линия GCC говорит:

<stdin>:1:16: error: array bound is not an integer constant before ‘]’ token

и ствол Clang говорит:

<stdin>:1:19: error: variable length arrays are a C99 feature [-Werror,-Wvla-extension]
void f() { int arr[(0, 42)]; }
                  ^

Как вы заметили, этот код действителен С++ 11. Тем не менее, запятые верхнего уровня все еще недействительны в С++ 11, потому что константное выражение в грамматике С++ 11 является условным выражением (где запятая верхнего уровня недопустима). Таким образом:

int arr[0, 42];

все еще плохо сформирован.