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.
C++11 flag
? – Bradytelicconstexpr int TryMe::arr[1];
outsidestruct TryMe
which makes it so there is no odr violation. OP's code lacks such a definition. – Rutherutheniaval
out ofmain()
you will get the error. – Rutheruthenia