23
#include <complex>

struct S
{
  static std::complex<double> constexpr c;
};

gcc generates an error because an initializer is missing. Clang and MSVC do not generate an error.

As far as I know a constexpr static data member must have an initializer, even if it is of class type that has a constructor that can be called without arguments (as in this case). Unfortunately I don't have the latest C++ standard to back up my assumption.

So the correct code should initialize with a constructor, for instance:

struct S
{
  static std::complex<double> constexpr c {};
};

Can anyone prove which compiler is right and which is wrong?

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
x y
  • 557
  • 3
  • 10

3 Answers3

11

GCC is wrong.

GCC uses the C++14 rules for constexpr variables, which requires an initializer to be provided. This is changed per P0386 (bold text is newly added text):

In 9.2.3.2p3, change:

If a non­-volatile n​on-­inline ​const static data member is of integral or enumeration type, its declaration in the class definition can specify a b​race­-or-­equal-­initializer​ in which every initializer-­clause​ that is an ​assignment-­expression​ is a constant expression (5.20). 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.​ A​n inline static data member can be defined in the class definition and may specify a b​race­-or-­equal­-initializer.​ 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.X). Declarations of other static data members shall not specify a b​race­-or-­equal-initializer.​

xskxzr
  • 12,442
  • 12
  • 37
  • 77
  • I think you are right. Clang 6.0.0 does not report an error in C++17 mode, but it does in C++14 mode and complains about a missing initializer for a constexpr static data member. So the rule seems to have changed from C++14 to C++17 (just as you have explained). gcc is wrong, and clang and MSVC (hard to believe) are right. Thanks for your explanation. – x y Jun 01 '18 at 14:04
  • Actually it depends on the standard. The OP didn't specify a particular version of C++, therefore gcc isn't neccessarily wrong - it depends on the language version (and compiler perhaps - maybe they fixed it in the meanwhile? I don't have the latest gcc version installed). – andreee Jun 11 '18 at 06:40
  • Has a bug been filed in GCC for this? – saxbophone Oct 21 '22 at 20:46
7

In this particular case, there are two answers:

  • For C++14, gcc is right (i.e. constexpr static data member must have an initializer).
  • For C++17 and beyond, gcc is wrong, since it refuses to compile conformant code.

Former case: In draft N3797 (C++14), 9.4.2.3 (Static data members) [class.static.data] (emphasis mine):

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.

See also: http://en.cppreference.com/w/cpp/language/static#Constant_static_members.

I said "in this particular case", because std::complex has a specialization for double that is a LiteralType. Therefore above rule applies. For general (i.e. non-literal) types, see codekaizers answer.

Latter case: For C++17, see xskxzr's answer.

andreee
  • 4,459
  • 22
  • 42
  • is `c` a [literal](https://timsong-cpp.github.io/cppwp/lex.literal)? if not, then that doesn't apply. – Joseph D. Jun 01 '18 at 07:58
  • 1
    For specialization of `double`, [`std::complex` is](https://en.cppreference.com/w/cpp/numeric/complex). – andreee Jun 01 '18 at 08:01
  • 1
    @codekaizer Wrong paragraph. You want [this](https://timsong-cpp.github.io/cppwp/basic.types#def:literal_type). – Passer By Jun 01 '18 at 14:54
  • @andreee You are right for C++11/14. But in C++17 the rule has changed (see the answer from xskxzr). In C++11/14 mode clang generates an error, too. – x y Jun 02 '18 at 06:11
  • @xy: Thanks for noting, updated! Please note that you didn't specify a specific language version, so there are two different answers (as you pointed out). – andreee Jun 11 '18 at 06:43
  • @andreee Yes, I forgot to specify C++17 in my original post. Sorry for the confusion... – x y Jul 07 '18 at 10:56
3

From dcl.constexpr#1:

A function or static data member declared with the constexpr specifier is implicitly an inline function or variable

constexpr static data members are implicitly inline.

Also from class#static.data-3, emphasis mine:

inline static data member may be defined in the class definition and may specify a brace-or-equal-initializer.


Thus, GCC is wrong. brace-or-equal-initializer is not strictly required.

Reference: N4659 C++17 Draft

Joseph D.
  • 11,804
  • 3
  • 34
  • 67
  • is there a GCC bug for this? Any details of "may specify". To me, "may specify" means that it _may not_, case in which the constexpr no-arg c-tor for that literal type does its job. But this doesn't work and GCC mandates an initialiser – haelix Jan 16 '19 at 17:23