Reputation: 505
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:
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.
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?
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++?
Upvotes: 1
Views: 341
Reputation: 119164
Regarding the example of std::strong_ordering
: the C++ standard itself states that these static data members are declared with const
and defined outside the class with inline constexpr
, so that should be considered strong evidence that this technique does, in fact, work in standard C++. See e.g. here.
I could find nothing in the standard that justifies Clang rejecting this only when it's templated. It appears that a bug has already been filed against Clang that identifies the specific issue, although it doesn't seem to have been confirmed by the maintainers.
Upvotes: 1