第一次使用A::a:
int a = A::a[0];
初始值设定项是一个常量表达式,但这并不能阻止A::a 在这里被odr-used。而且,确实,A::a 被这个表达式odr-used。
从表达式A::a[0]开始,我们来看看[basic.def.odr](3.2)/3(对于未来的读者,我使用的是N3936中的措辞): p>
一个变量x [在我们的例子中,A::a],其名称显示为一个潜在的评估表达式 ex [在我们的例子中,id-expression A::a] 是 odr-used 除非
那么:e 的可能值有哪些?表达式的潜在结果集是该表达式的一组子表达式(您可以通过阅读 [basic.def.odr](3.2)/2 来检查这一点),所以我们只需要考虑 ex 是子表达式的表达式。它们是:
A::a
A::a[0]
其中,左值到右值的转换不会立即应用于A::a,因此我们只考虑A::a[0]。根据 [basic.def.odr](3.2)/2,A::a[0] 的潜在结果集是空的,因此 A::a 是 odr-used表达。
现在,您可以争辩说我们首先将A::a[0] 重写为*(A::a + 0)。但这并没有改变:e 的可能值是
A::a
A::a + 0
(A::a + 0)
*(A::a + 0)
其中,只有第四个应用了左值到右值的转换,并且再次,[basic.def.odr](3.2)/2 表示这组潜在结果*(A::a + 0) 为空。特别要注意,数组到指针的衰减不是左值到右值的转换 ([conv.lval](4.1)),即使它转换了数组左值到指针右值——这是数组到指针的转换([conv.array](4.2))。
第二次使用A::a:
int b [A::a[1]];
根据标准,这与第一种情况没有什么不同。同样,A::a[1] 是一个常量表达式,因此这是一个有效的数组绑定,但仍然允许编译器在运行时发出代码来计算这个值,并且数组绑定仍然 odr-uses @ 987654350@.
请特别注意,常量表达式(默认情况下)是可能求值的表达式。根据[basic.def.odr](3.2)/2:
一个表达式可能被计算,除非它是一个未计算的操作数(第 5 条)或其子表达式。
[expr](5)/8 只是将我们重定向到其他子条款:
在某些情况下,会出现未计算的操作数(5.2.8、5.3.3、5.3.7、7.1.6.2)。未计算的操作数不会被计算。
这些子条款说(分别)某些typeid 表达式的操作数、sizeof 的操作数、noexcept 的操作数和decltype 的操作数是未计算的操作数。没有其他类型的未计算操作数。