Разрешен ли оператор запятой в константном выражении в С++ 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.
Ответы
Ответ 1
Да, я считаю, что это изменение между C++ 03 и C++ 11. Я полагаю, что это было сделано примерно по той причине, на которую вы намекаете - что нет особенно веской причины, по которой оператор запятой не может быть частью константного выражения.
Я считаю, что правило в 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];
все еще плохо сформирован.