1

The following piece of code tries to provide a constexpr static data member of type X that belongs to the type X itself. In the standard library (C++20), there seem to be such examples with (at least) the class 'std::strong_ordering' and its few static constexpr members named 'equal', 'less', 'greater' and 'equivalent'. I am wondering if (and how) this can be achieved without compiler magic.

A direct declaration (including the definition) does not seem to work with any compiler nor to be valid C++. That being said, a declaration as 'const' followed later (outside of the class) by a definition as 'constexpr' seems to work fine with GCC and at least in some cases with Clang.

My questions are the following:

  1. Is the trick consisting of a "const declaration" followed by a "constexpr definition" forms valid C++ code that actually provides a valid static constexpr data member of type X inside the class X itself?
  2. The non template version (type Foo) compiles with GCC and Clang while the template version (type Bar<0>) only compiles with GCC. Is there any rule that would make the non template version valid C++ code and the template one non valid C++ code?
  3. Can the error generated by Clang be considered as a compiler bug?

Source code (C++17):

// With this non template struct,
// It compiles successfully with GCC and Clang.
struct Foo
{
  // A data member.
  int val;

  // A static data member.
  // It is declared here as 'const'
  // and defined below as 'constexpr'.
  static Foo const instance;

  // A constexpr constructor.
  constexpr Foo(int value) noexcept : val{ value } {}
};

// With this non template struct,
// It compiles successfully with GCC
// but it generates an error with Clang.
template<int N>
struct Bar
{
  // A data member.
  int val;

  // A static data member.
  // It is declared here as 'const'
  // and defined below as 'constexpr'.
  static Bar const instance;

  // A constexpr constructor.
  constexpr Bar(int value) noexcept : val{ value } {}
};

// Definition of the static
// data member of the struct Foo.
// Note that it is defined here as 'constexpr'
// while it was declared above only as 'const'.
constexpr Foo const Foo::instance{32};

// Definition of the static data
// member of the template struct Foo.
// Note that it is defined here as 'constexpr'
// while it was declared above only as 'const'.
template<int N>
constexpr Bar<N> const Bar<N>::instance{32};

// The main function.
int main()
{
  // Init a constexpr const reference to
  // the static data member object of type Foo.
  constexpr Foo const& foo{ Foo::instance };

  // Init a constexpr const reference to
  // the static data member object of type Bar<0>.
  constexpr Bar<0> const& bar{ Bar<0>::instance };

  // This compile time check
  // works fine with GCC and Clang.
  static_assert(foo.val == 32);

  // This compile time check works fine with GCC
  // but generates a compilation error with Clang.
  static_assert(bar.val == 32);

  // Return zero.
  return 0;
};

Previous StackOverflow related questions:

I am quoting two StackOverflow questions which are related but (in my view) do not clearly answer my question.

  1. This one tries to achieve the same goal as me but does not seem to mention the "const declaration / constexpr definition" trick. Can't a class have static constexpr member instances of itself?

  2. This one mentions the "const declaration / constexpr definition" trick but is not clearly answering the question whether it is valid C++ code or not. Static const declaration, constexpr definition of variable, valid c++?

Ankur deDev
  • 395
  • 1
  • 7

0 Answers0