n3301,6.6常量表达式,p4(重点添加):
每个常量表达式的计算结果应为其类型可表示值范围内的常量。
n3301,6.5.15逻辑或运算符,p4(强调添加):
如果第一个操作数比较不等于 0,则不评估第二个操作数。
问题:为什么第一个要求不依赖于这种常量表达式的评估(或非评估)的要求?
换句话说:为什么“每个常量表达式应计算为......”而不是“每个计算的常量表达式应计算为......”?
这是一个疏忽吗?
问题的原因:有些人认为“每个常量表达式应计算为……”优先于“第二个操作数不被计算”。
是的,n3301有脚注119:
- 因此,在下面的初始化中,
static int i = 2 || 1 / 0;
该表达式是有效的整数常量表达式,其值为 1。
然而,“在ISO标准中,注释无一例外都是非规范性的。 ”
有问题的引文是常量表达式定义的一部分,语法规则用于预期在编译时评估代码的地方。这意味着常量表达式通常是无条件评估的(在编译时)。引入不被评估的常量表达式的概念是无益且不必要的。
2 || 1 / 0
是一个有效的常量表达式,因为它是一个满足常量表达式约束的有效条件表达式。相关地,它的计算结果为
1
,因此它确实“计算结果为在其类型可表示值范围内的常数”。评价
2 || 1 / 0
并不评价1 / 0
,但这并不意味着2 || 1 / 0
不能评价。因此,
2 || 1 / 0
可以在需要常量表达式的地方使用。例如,case 2 || 1 / 0
是有效的(尽管很奇怪)。编译器资源管理器上的演示。
虽然
1 / 0
是一个有效的条件表达式,但它不符合常量表达式的约束,所以它不是一个有效的常量表达式。具体来说,它不会“计算处于其类型可表示值范围内的常数”。
因此,
1 / 0
不能在需要常量表达式的地方使用。例如,case 1 / 0
是无效的。编译器资源管理器上的演示。
问题的一部分是(非规范性)注释中的常量表达式是什么:
静态 int i = 2 || 1 / 0;
如果它是整个表达式
i = 2 || 1 / 0
,那么它就可以被评估,因为从左到右的快捷规则(a || b
如果 a 为真,则不评估 b)意味着常量表达式确实在可表示范围内评估为常量。尽管子表达式根本
1 / 0
无法被求值,更不用说可以表示为常量了……我确实明白原帖作者的意思。我想我推断的是常量表达式是赋值的整个右侧。这是有用的解释。
我认为规范没有完全明确常量表达式是什么。“常量表达式是可以在翻译过程中求值的表达式”之类的说法会有所帮助,这与“常量表达式可以在翻译过程中求值”不同。我认为“可以”不同于是、必须甚至应该。
该规范继续指定常量表达式,甚至允许依赖于实现的扩展。
它还说“常量表达式的评估语义规则与非常量表达式的评估语义规则相同。”并提供了有关上面给出的示例的(非规范性)注释。
但从理论上讲,这仍然(至少在原则上)并不与(子)表达式
1/0
符合常量表达式的条件相矛盾。这绝对是一个语言律师的问题,因为我认为熟悉 C 的人都知道作者的意图,问题在于他们是否非常明确和准确地表达了这一点。