I want to have a static const
char
array in my class. GCC complained and told me I should use constexpr
, although now it\'s telling me
C++17 fixes this problem for constexpr static
member variables requiring an out-of-line definition if it was odr-used. See the second half of this answer for pre-C++17 details.
Proposal P0386 Inline Variables introduces the ability to apply the inline
specifier to variables. In particular to this case constexpr
implies inline
for static member variables. The proposal says:
The inline specifier can be applied to variables as well as to functions. A variable declared inline has the same semantics as a function declared inline: it can be defined, identically, in multiple translation units, must be defined in every translation unit in which it is odr-used, and the behavior of the program is as if there is exactly one variable.
and modified [basic.def]p2:
A declaration is a definition unless
...
- it declares a static data member outside a class definition and the variable was defined within the class with the constexpr specifier (this usage is deprecated; see [depr.static_constexpr]),
...
and add [depr.static_constexpr]:
For compatibility with prior C++ International Standards, a constexpr static data member may be redundantly redeclared outside the class with no initializer. This usage is deprecated. [ Example:
struct A { static constexpr int n = 5; // definition (declaration in C++ 2014) }; constexpr int A::n; // redundant declaration (definition in C++ 2014)
— end example ]
In C++03, we were only allowed to provide in-class initializers for const integrals or const enumeration types, in C++11 using constexpr
this was extended to literal types.
In C++11, we do not need to provide a namespace scope definition for a static constexpr
member if it is not odr-used, we can see this from the draft C++11 standard section 9.4.2
[class.static.data] which says (emphasis mine going forward):
[...]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.
So then the question becomes, is baz
odr-used here:
std::string str(baz);
and the answer is yes, and so we require a namespace scope definition as well.
So how do we determine if a variable is odr-used? The original C++11 wording in section 3.2
[basic.def.odr] 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.
So baz
does yield a constant expression but the lvalue-to-rvalue conversion is not immediately applied since it is not applicable due to baz
being an array. This is covered in section 4.1
[conv.lval] which says :
A glvalue (3.10) of a non-function, non-array type T can be converted to a prvalue.53 [...]
What is applied in the array-to-pointer conversion.
This wording of [basic.def.odr] was changed due to Defect Report 712 since some cases were not covered by this wording but these changes do not change the results for this case.