给出以下代码:
struct A { static constexpr int a[3] = {1,2,3}; };
int main () {
int a = A::a[0];
int b [A::a[1]];
}
A::a
是否一定在int a = A::a[0]
中是odr-used?注意:此问题表示a debate in the Lounge的较不易理解/不合逻辑/无尽的版本。
最佳答案
第一次使用A::a
:
int a = A::a[0];
初始值设定项是一个常量表达式,但这并不能阻止
A::a
在此处被奇特使用。实际上,A::a
被该表达式使用。从表达式
A::a[0]
开始,让我们遍历[basic.def.odr](3.2)/ 3(对于以后的读者,我使用的是N3936的措辞):变量
x
[在我们的情况下为A::a
]的名称显示为一个可能评估的表达式,例如[在本例中,id-expression A::a
]除非使用,否则将左值到右值转换应用于
x
会产生一个常量表达式[确实如此]不会调用任何非平凡的函数[不会],并且,
如果
x
是一个对象[它是],ex
是表达式e
的一组可能结果的元素,其中将左值到右值转换应用于e
,或者e
是舍弃值表达式。那么:
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
。现在,您可能会说我们首先将
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使用A::a
。特别要注意的是,常量表达式(默认情况下)是可能求值的表达式。根据[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
的操作数是未求值的操作数。没有其他类型的未评估操作数。