Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Static const declaration, constexpr definition of variable, valid c++?

Tags:

c++

Example: In header file:

class Foo
{
     static const int IntArray[];                         
};

In source file:

constexpr int Foo::IntArray[] = { 1, 2, 3, 4 };

This compiles on g++ and allows me to put the initializer list in the source file in stead of the header. (if it were constexpr in the header the compiler requires immediate initialization in the header). While still allowing the array to be used in constexpr evaluations...

Is this valid, portable C++ ?

like image 468
Unimportant Avatar asked Apr 02 '17 19:04

Unimportant


2 Answers

The Right Way

Before we begin the language-lawyering, the correct approach is to do it the other way around. In the header file:

class Foo
{
     static constexpr int IntArray[] = { 1, 2, 3, 4 };
};

And then in a source file:

constexpr int Foo::IntArray[];

If you declare a static constexpr class data member in the class definition, you must initialize it then and there. This is optional for static const data members. If you use the static constexpr data member anywhere in the program, you must give a definition like the one above, in exactly one source file, with no initializer.

What the (Draft) Standard Says

The example code in the question is bad style, and apparently at least one compiler rejects it, but it does in fact seem to comply with the C++14 draft standard. [dcl/constexpr] says:

The constexpr specifier shall be applied only to the definition of a variable or variable template, the declaration of a function or function template, or the declaration of a static data member of a literal type. If any declaration of a function, function template, or variable template has a constexpr specifier, then all its declarations shall contain the constexpr specifier.

Notice whose declarations are, by omission, not all required to contain the constexpr specifier.

Later in the same section:

A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. [...]

But see also [class.static.data]:

If a non-volatile const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. 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 in the program and the namespace scope definition shall not contain an initializer.

In this context, the odr in “odr-used” stands for the one-definition-rule and means “whose name appears as a potentially-evaluated expression.” ([basic.def.odr]) The last sentence means that, if you declare, static constexpr int foo = 0; in the class definition, and you will later use it in an expression, such as int x = MyClass::foo;, then one and only one source file needs to have a line like constexpr int MyClass::foo; in it, so the linker knows which object file to put it in.

like image 61
Davislor Avatar answered Nov 12 '22 04:11

Davislor


I doubt it's compliant. The declaration and definition are required to be identical AFAIK.

It's certainly not portable. Although gcc, clang and microsoft cl 2017 accept it,

ICC reports:

<source>(6): error: member "Foo::IntArray" (declared at line 3) was previously not declared constexpr
  constexpr int Foo::IntArray[] = { 1, 2, 3, 4 };
  ^
compilation aborted for <source> (code 2)
Compiler exited with result code 2
like image 3
Richard Hodges Avatar answered Nov 12 '22 04:11

Richard Hodges