Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is the constexpr specifier required on the declaration of a constexpr static member initialized outside of the class?

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 the constexpr specifier is implicitly an inline function or variable (10.1.6). If any declaration of a function or function template has a constexpr specifier, then all its declarations shall contain the constexpr 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 as const. Such an object shall have literal type and shall be initialized. In any constexpr 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 constness, 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 the constexpr 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.

like image 703
monkey0506 Avatar asked May 21 '18 00:05

monkey0506


People also ask

Is static needed for constexpr?

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?

Can constexpr be extern?

Actually, extern constexpr does make sense, and would be useful. In particular, static constexpr class member variables automatically have external linkage (which is a huge inconsistency and gotcha - people often forget to add a definition in a . cc file).

Why is constexpr needed?

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.

What does constexpr mean in C++?

constexpr stands for constant expression and is used to specify that a variable or function can be used in a constant expression, an expression that can be evaluated at compile time. The key point of constexpr is that it can be executed at compile time.

What is a constexpr specifier in C++?

A constexpr specifier used in an object declaration or non-static member function (until C++14) implies const. A constexpr specifier used in a function or static data member (since C++17) declaration implies inline. If any declaration of a function or function template has a constexpr specifier, then every declaration must contain that specifier.

When can a reference be declared as constexpr?

A reference may be declared as constexpr when both these conditions are met: The referenced object is initialized by a constant expression, and any implicit conversions invoked during initialization are also constant expressions. All declarations of a constexpr variable or function must have the constexpr specifier.

What is not initialized in constexpr?

Not initialized int j = 0; constexpr int k = j + 1; //Error! j not a constant expression A constexpr function is one whose return value is computable at compile time when consuming code requires it. Consuming code requires the return value at compile time to initialize a constexpr variable, or to provide a non-type template argument.

What are the rules for a constexpr function or constructor?

A constexpr function or constructor is implicitly inline. The following rules apply to constexpr functions: A constexpr function must accept and return only literal types. A constexpr function can be recursive. It can't be virtual. A constructor can't be defined as constexpr when the enclosing class has any virtual base classes.


1 Answers

If I were to read this:

static S const ZERO; // not marked `constexpr`, but still `const`

S::ZERO will never change its value during run-time due to const.

However:

constexpr S S::ZERO{ 0 }; // implicitly `inline` (if C++17) and `const`

Constant Evaluation is done for S::ZERO which will have constant integral value 0 for _value.
This invokes your constexpr constructor:

constexpr S(int value = {}) : _value{ value } {}

As per basic.start.static - Constant Initialization:

A constant initializer for a variable or temporary object o is an initializer whose full-expression is a constant expression, except that if o is an object, such an initializer may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types.

AND expr.const/8.7 - Constant Evaluation:

a variable whose name appears as a potentially constant evaluated expression that is either a constexpr variable or is of non-volatile const-qualified integral type or of reference type.

Therefore:

Is it a violation to have non-matching use of the constexpr specifier across variable declarations and definitions?

I believe your code is fine.

like image 115
Joseph D. Avatar answered Oct 14 '22 23:10

Joseph D.