是一个constexpr数组必须odr使用下标? [英] Is a constexpr array necessarily odr-used when subscripted?
问题描述
给定以下代码:
struct A {static constexpr int a [3] = {1,2,3} ; };
int main(){
int a = A :: a [0];
int b [A :: a [1]];
}
是 A :: a
必须 odr使用 int a = A :: a [0]
?
首次使用 A :: a
:
int a = A :: a [0 ];
初始化程序是一个常量表达式,但不会停止 A: :a
从这里使用 odr使用。而且, A :: a
是这个表达式所使用的 odr-use 。
从 A :: a [0]
开始,让我们来看看 [basic.def.odr](3.2)/ 3 未来读者,我使用N3936的措辞):
变量
x
在我们的例子中,A :: a
],其名称显示为可能评估的表达式ex [A :: a
]是 odr使用,除非
将左值到右值转换应用到
x
产生一个常量表达式[b]
不调用任何非平凡函数[ it does not] and,
如果
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
是由此表达式使用的 odr-used 。
您可以认为我们首先将 A :: a [0]
重写为 *(A :: a + 0)
。但是没有任何变化: e
的可能值为
A :: a
A :: a + 0
(A :: a + 0)
*(A :: a + 0)
其中,只有第四个应用了左值到右值转换,并且 [basic.def.odr] )/ 2 表示 *(A :: a + 0)
的潜在结果集为空。特别要注意的是,数组到指针衰减是不左值到右值转换( [conv.lval](4.1)),即使它转换数组左值到指针rvalue - 这是一个数组到指针的转换( [conv.array](4.2))。
使用 A :: a
:
a [1]];
根据标准,这与第一种情况没有什么不同。再次, A :: a [1]
是一个常量表达式,因此这是一个有效的数组边界,但是编译器仍然允许在运行时发出代码来计算这个值,并且数组仍然 odr使用 A :: a
。
注特别是常数表达式(默认)是潜在评估的表达式。每 [basic.def.odr](3.2)/ 2 :
[expr](expr) 5)/ 8 只是将我们重定向到其他子类别:
在某些上下文中, .3,5.3.7,7.1.6.2)。
这些子句分别表示一些 typeid
sizeof
的操作数, noexcept
的操作数和 decltype
是未求值的操作数。没有其他类型的未经评估的操作数。 Given the following code:
struct A { static constexpr int a[3] = {1,2,3}; };
int main () {
int a = A::a[0];
int b [A::a[1]];
}
is A::a
necessarily odr-used in int a = A::a[0]
?
Note: This question represents a less flamey/illogical/endless version of a debate in the Lounge.
First use of A::a
:
int a = A::a[0];
The initializer is a constant expression, but that doesn't stop A::a
from being odr-used here. And, indeed, A::a
is odr-used by this expression.
Starting from the expression A::a[0]
, let's walk through [basic.def.odr](3.2)/3 (for future readers, I'm using the wording from N3936):
A variable
x
[in our case,A::a
] whose name appears as a potentially-evaluated expression ex [in our case, the id-expressionA::a
] is odr-used unless
applying the lvalue-to-rvalue conversion to
x
yields a constant expression [it does] that does not invoke any non-trivial functions [it does not] and,if
x
is an object [it is],
ex
is an element of the set of potential results of an expressione
, where either the lvalue-to-rvalue conversion is applied toe
, ore
is a discarded-value expression.
So: what possible values of e
are there? The set of potential results of an expression is a set of subexpressions of the expression (you can check this by reading through [basic.def.odr](3.2)/2), so we only need to consider expressions of which ex
is a subexpression. Those are:
A::a
A::a[0]
Of these, the lvalue-to-rvalue conversion is not applied immediately to A::a
, so we only consider A::a[0]
. Per [basic.def.odr](3.2)/2, the set of potential results of A::a[0]
is empty, so A::a
is odr-used by this expression.
Now, you could argue that we first rewrite A::a[0]
to *(A::a + 0)
. But that changes nothing: the possible values of e
are then
A::a
A::a + 0
(A::a + 0)
*(A::a + 0)
Of these, only the fourth has an lvalue-to-rvalue conversion applied to it, and again, [basic.def.odr](3.2)/2 says that the set of potential results of *(A::a + 0)
is empty. In particular, note that array-to-pointer decay is not an lvalue-to-rvalue conversion ([conv.lval](4.1)), even though it converts an array lvalue to a pointer rvalue -- it's an array-to-pointer conversion ([conv.array](4.2)).
Second use of A::a
:
int b [A::a[1]];
This is no different from the first case, according to the standard. Again, A::a[1]
is a constant expression, thus this is a valid array bound, but a compiler is still permitted to emit code at runtime to compute this value, and the array bound still odr-uses A::a
.
Note in particular that constant expressions are (by default) potentially-evaluated expressions. Per [basic.def.odr](3.2)/2:
An expression is potentially evaluated unless it is an unevaluated operand (Clause 5) or a subexpression thereof.
[expr](5)/8 just redirects us to other subclauses:
In some contexts, unevaluated operands appear (5.2.8, 5.3.3, 5.3.7, 7.1.6.2). An unevaluated operand is not evaluated.
These subclauses say that (respectively) the operand of some typeid
expressions, the operand of sizeof
, the operand of noexcept
, and the operand of decltype
are unevaluated operands. There are no other kinds of unevaluated operand.
这篇关于是一个constexpr数组必须odr使用下标?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!