n3301, 6.6 Константные выражения, п4 (выделено нами):
Каждое константное выражение должно оценивать константу, находящуюся в диапазоне представимых значений для его тип.
n3301, 6.5.15 Логический оператор ИЛИ, p4 (выделено нами):
Если первый операнд при сравнении не равен 0, второй операнд не оценивается.
Вопрос: почему 1-е требование не зависит от требования вычисления (или невычисления) такого константного выражения?
Другими словами: почему «Каждое константное выражение должно иметь значение...» вместо «Каждое вычисленное константное выражение должно иметь значение...»?
Это упущение?
Причина вопроса: некоторые люди считают, что «Каждое константное выражение должно иметь значение...» имеет приоритет (т. е. более сильное, имеет больший приоритет) над «вторым операндом не оценивается».
Да, у n3301 есть сноска 119:
- Таким образом, при следующей инициализации
static int i = 2 || 1 / 0;
выражение является допустимым целочисленным константным выражением со значением один.
Однако «В стандартах ISO примечания без исключения являются ненормативными.»
Как вы думаете, почему 2 || 1 / 0
не является константным выражением? Он «вычисляет константу, которая находится в диапазоне представимых значений для ее типа. Другими словами, можете ли вы привести пример константного выражения, которое не вычисляется?
@ikegami не является операндом и выражением? Тогда это правило будет применяться к каждому из задействованных выражений. И 1/0
никогда не будет оценен.
@Gerhard, Да, это было бы правильное выражение. Но что с того? Тот факт, что 1 / 0
не является допустимым константным выражением (например, нельзя использовать case 1 / 0
), не означает, что оно не может быть его частью.
Цитата, о которой идет речь, является частью определения константного выражения. Правило грамматики используется в тех местах, где ожидается, что код будет оцениваться во время компиляции. Это означает, что константные выражения обычно оцениваются безоговорочно (во время компиляции). Введение концепции константного выражения, которое не вычисляется, бесполезно и ненужно.
2 || 1 / 0
является допустимым константным выражением, поскольку это допустимое условное выражение, удовлетворяющее ограничениям константного выражения.
Соответственно, его значение равно 1
, поэтому оно «вычисляет константу, находящуюся в диапазоне представимых значений для своего типа».
Оценка 2 || 1 / 0
не оценивает 1 / 0
, но это не значит, что 2 || 1 / 0
нельзя оценить.
Следовательно, 2 || 1 / 0
можно использовать там, где ожидается константное выражение. Например, case 2 || 1 / 0
допустимо (хотя и довольно странно).
Демо в Compiler Explorer.
Хотя 1 / 0
является допустимым условным выражением, оно не соответствует ограничениям константного выражения, поэтому оно не является допустимым константным выражением.
В частности, он не «вычисляет константу, находящуюся в диапазоне представимых значений для ее типа».
Следовательно, 1 / 0
нельзя использовать там, где ожидается константное выражение. Например, case 1 / 0
недопустимо.
Демо в Compiler Explorer.
Спасибо. Я перепутал «постоянное выражение» с «постоянным подвыражением».
Дополнительные вопросы: 1) Означает ли это, что стандарт C не требует, чтобы каждое константное подвыражение возвращало константу, находящуюся в диапазоне представимых значений для ее типа? 2) Приоритет оператора /
выше приоритета оператора ||
. Означает ли это, что при вычислении 2 || 1 / 0
компилятор C может сначала оценить 1 / 0
, скажем, <invalid>
, а затем оценить 2 || <invalid>
как 2? 3) В каком порядке вычисляет типичный компилятор C (например, GCC, Clang, MSVC, ICC) 2 || 1 / 0
?
1) Не существует правила под названием «подвыражение» или «постоянное подвыражение». 2) Нет, из-за короткого замыкания ||
. RHS оценивается только в том случае, если LHS истинен. В своем вопросе вы процитировали соответствующее правило. 3) Он не должен оценивать правую часть ||
. Поэтому он должен создавать код, который генерирует 1
.
Часть вопроса заключается в том, каково постоянное выражение в (ненормативном) примечании:
статический int я = 2 || 1/0;
Если это все выражение i = 2 || 1 / 0
, то его можно вычислить, поскольку правило сокращения слева направо (что в a || b
, если a истинно, то b не оценивается) означает, что константное выражение действительно оценивается как константа в представимом диапазоне.
И это несмотря на то, что подвыражение 1 / 0
не может быть вычислено (вообще), не говоря уже о константе, которую можно представить...
Я понимаю точку зрения ОП. Я думаю, что делаю вывод, что постоянное выражение — это вся правая часть присваивания. Это полезная интерпретация.
Я не думаю, что спецификация не совсем понимает, что такое постоянное выражение. Помогло бы что-то вроде «Константное выражение — это выражение, которое может быть вычислено во время перевода», и это отличается от «Константное выражение может быть вычислено во время перевода». Я читаю «можно» в отличие от «есть», «должен» или даже «должен».
Спецификация далее определяет константные выражения и даже допускает расширения, зависящие от реализации.
Там также говорится: «Семантические правила для вычисления постоянного выражения такие же, как и для непостоянного выражения». выражения». и содержит (ненормативное) примечание к приведенному выше примеру.
Но педантично, что (по крайней мере, в принципе) это не противоречит тому, что (суб-)выражение 1/0
квалифицируется как постоянное выражение.
Это определенно вопрос языкового юриста, потому что я думаю, что люди, знакомые с C, знают намерение авторов, вопрос в том, достигли ли они его совершенно однозначно и точно.
6.5.15 < 6.6
... так логично или имеет приоритет над постоянным выражением. Просто шучу :)