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... так логично или имеет приоритет над постоянным выражением. Просто шучу :)