Static constexpr odr-used or not?

TryMe::arr is odr-used but you don’t provide a definition (see it live):

constexpr int TryMe::arr[1];

Why is the result inconsistent between gcc and clang? This is because odr violations do not require a disagnostic, from both the C++11 and C++14 draft standard (emphasis mine):

Every program shall contain exactly one definition of every non-inline
function or variable that is odr-used in that program; no diagnostic
required
.

We can see it is odr-used from the draft C++11 standard, section 3.2 which says:

An expression is potentially evaluated unless it is an unevaluated
operand (Clause 5) or a subexpression thereof. 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.

TryMe::arr is an object and it does satisfy the requirements for appearing in a constant expression but the lvalue-to-rvalue conversion is not immediately applied to TryMe::arr but to TryMe::arr[0].

The updated wording from the draft C++14 standard which applies to C++11 as well since it was applied via a defect report(DR 712):

A variable x whose name appears as a potentially-evaluated expression
ex is odr-used unless applying the lvalue-to-rvalue conversion (4.1)
to x yields a constant expression (5.19) that does not invoke any
non-trivial functions and, if x is an object, ex is an element of the
set of potential results of an expression e, where either the
lvalue-to-rvalue conversion (4.1) is applied to e, or e is a
discarded-value expression

The potential results of the expression TryMe::arr[0] is empty by the criteria in 3.2 paragraph 2 and so it is odr-used.

Note: you need to provide a definition outside of the class as per section 9.4.2 [class.static.data] which says (emphasis mine):

A static data member of literal type can be declared in the class
definition with the constexpr specifier; if so, its declaration shall
specify a brace-or-equal-initializer in which every initializer-clause
that is an assignment-expression is a constant expression. [ Note: In
both these cases, the member may appear in constant expressions. —end
note ] The member shall still be defined in a namespace scope if it
is odr-used (3.2)
in the program and the namespace scope definition
shall not contain an initializer

Update

T.C. pointed out defect report 1926 which adds the following bullet to 3.2 [basic.def.odr] paragraph 2:

  • If e is a subscripting operation (5.2.1 [expr.sub]) with an array operand, the set contains that operand.

Which means subscripting an array is no longer an odr-use and so the OPs code would be well-formed in C++1z and it seems like C++14 since the defect looks like it is against C++14.

Leave a Comment