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
: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.
No, it is not odr-used.
First, both your array and its elements are of literal type:
[C++11: 3.9/10]:
A type is a literal type if it is:
- a scalar type; or
- a class type (Clause 9) with
- a trivial copy constructor,
- no non-trivial move constructor,
- a trivial destructor,
- a trivial default constructor or at least one constexpr constructor other than the copy or move constructor, and
- all non-static data members and base classes of literal types; or
- an array of literal type.
Now we look up the odr-used rules:
[C++11: 3.2/2]:
[..] A variable or non-overloaded function whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied. [..]
And here we've been referred to the rules on constant expressions, which contain nothing prohibiting your initialiser from being a constant expression; the pertinent passages are:
[C++11: 5.19/2]:
A conditional-expression is a constant expression unless it involves one of the following as a potentially evaluated subexpression [..]:
- [..]
- an lvalue-to-rvalue conversion (4.1) unless it is applied to
- a glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression, or
- a glvalue of literal type that refers to a non-volatile object defined with
constexpr
, or that refers to a sub-object of such an object, or- a glvalue of literal type that refers to a non-volatile temporary object initialized with a constant expression;
- [..]
(Don't be put off by the name of the production, "conditional-expression": it is the only production of constant-expression and is thus the one we're looking for.)
Then, thinking about the equivalence of A::a[0]
to *(A::a + 0)
, after the array-to-pointer conversion you have an rvalue:
[C++11: 4.2/1]:
An lvalue or rvalue of type "array ofN
T
" or "array of unknown bound ofT
" can be converted to a prvalue of type "pointer toT
". The result is a pointer to the first element of the array.
Your pointer arithmetic is then performed on this rvalue and the result is also an rvalue, used to initialise a
. No lvalue-to-rvalue conversion here whatsoever, so still nothing violating "the requirements for appearing in a constant expression".
A::a
is odr-used.In C++11, the relevant wording is 3.2p2 [basic.def.odr]:
[...] A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied. [...]
The name of the variable A::a
appears in the declaration int a = A::a[0]
, in the full-expression A::a[0]
, which is a potentially-evaluated expression. A::a
is:
However, the lvalue-to-rvalue conversion is not immediately applied to A::a
; it is applied to the expression A::a[0]
. Indeed, lvalue-to-rvalue conversion may not apply to an object of array type (4.1p1).
So A::a
is odr-used.
Since C++11, the rules have been broadened somewhat. DR712 Are integer constant operands of a conditional-expression "used?" introduces the concept of the set of potential results of an expression, which allows expressions such as x ? S::a : S::b
to avoid odr-use. However, while the set of potential results respects such operators as the conditional operator and comma operator, it does not respect indexing or indirection; so A::a
is still odr-used in the current drafts for C++14 (n3936 as of date).
[I believe this is a condensed equivalent to Richard Smith's answer, which however does not mention the change since C++11.]
At When is a variable odr-used in C++14? we discuss this issue and possible wording changes to section 3.2 to allow indexing or indirecting an array to avoid odr-use.