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.
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)).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With