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.