How come that the following works on gcc
but doesn't on clang
, (see it live):
constexpr int giveMeValue() { return 42; }
struct TryMe {
static constexpr int arr[1] = {
giveMeValue()
};
};
int main() {
int val = TryMe::arr[0];
return val;
}
I get an unresolved external symbol with clang.
Is TryMe::arr[0]
an object? If it is, is it odr-used?
A static constexpr variable has to be set at compilation, because its lifetime is the the whole program. Without the static keyword, the compiler isn't bound to set the value at compilation, and could decide to set it later. So, what does constexpr mean?
In plain word, odr-used means something(variable or function) is used in a context where the definition of it must be present.
A constexpr integral value can be used wherever a const integer is required, such as in template arguments and array declarations. And when a value is computed at compile time instead of run time, it helps your program run faster and use less memory.
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.
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