C++17 §10.1.5/1 states:
The
constexpr
specifier shall be applied only to the definition of a variable or variable template or the declaration of a function or function template. A function or static data member declared with theconstexpr
specifier is implicitly an inline function or variable (10.1.6). If any declaration of a function or function template has aconstexpr
specifier, then all its declarations shall contain theconstexpr
specifier.
A similar paragraph has existed in the standard since C++11 (§7.1.5/1), which is cited in a comment by Richard Smith, in which he contends that the C++ Standard does not require the constexpr
specifier to match between the declaration and definition of a variable. The last statement of the above paragraph explicitly requires the constexpr
specifier to match across function and function template declarations, but does not mention variable declarations.
§10.1.5/9 states:
A
constexpr
specifier used in an object declaration declares the object asconst
. Such an object shall have literal type and shall be initialized. In anyconstexpr
variable declaration, the full-expression of the initialization shall be a constant expression (8.20).
Of course if we have a separate declaration and definition, they will both need to match in const
ness, regardless of whether the constexpr
specifiers are required to match.
§12.2.3.2/2-3 says:
2 The declaration of a non-inline static data member in its class definition is not a definition and may be of an incomplete type other than cv
void
. The definition for a static data member that is not defined inline in the class definition shall appear in a namespace scope enclosing the member’s class definition. In the definition at namespace scope, the name of the static data member shall be qualified by its class name using the :: operator. The initializer expression in the definition of a static data member is in the scope of its class (6.3.7).3 If a non-volatile non-inline
const
static data member is of integral or enumeration type... If the member is declared with theconstexpr
specifier, it may be redeclared in namespace scope with no initializer (this usage is deprecated; see D.1). Declarations of other static data members shall not specify a brace-or-equal-initializer.
§D.1/1 reads:
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.
From which we can gather that if the member is declared with the constexpr
specifier, then a namespace scope definition is redundant and the initializer expression must be paired with the declaration and must be omitted from the definition/redeclaration.
To serve as a complete example, I offer up the case of a static member of its own literal type class (which cannot be initialized in-class):
struct S
{
static S const ZERO; // not marked `constexpr`, but still `const`
constexpr S(int value = {}) : _value{ value } {}
int const _value;
};
constexpr S S::ZERO{ 0 }; // implicitly `inline` (if C++17) and `const`
This interpretation of constexpr
use with static data members is supported by GCC, Clang, and MSVC, though I have been told that this is wrong.
Is it a violation to have non-matching use of the constexpr
specifier across variable declarations and definitions?
If this is in fact a violation, then it is impossible to correctly define a constexpr
static data member of its own class, as in-class definitions are prohibited because the type is incomplete and out-of-class definitions are prohibited from including an initializer if the in-class declaration is marked with the constexpr
specifier.