I have a class:
#include <array>
template<class T, std::size_t N>
requires std::is_arithmetic_v<T> && (N >= 1)
class Vector
{
static constexpr std::size_t Dimension = N;
std::array<T, Dimension> Elements;
public:
constexpr Vector() noexcept : Elements{} {}
constexpr ~Vector() = default;
static constexpr Vector ZeroVector{};
};
int main()
{
Vector<float, 7> boo = Vector<float, 7>::ZeroVector;
}
The above fails to compile on compiler explorer with MSVC and Clang (trunk) with C++23 compiler flags, but it compiles on GCC (trunk) with C++23 compiler flags.
Clang gives the following error:
<source>:13:29: error: constexpr variable cannot have non-literal type 'const Vector<float, 7>'
13 | static constexpr Vector ZeroVector{};
| ^
<source>:18:28: note: in instantiation of template class 'Vector<float, 7>' requested here
18 | Vector<float, 7> boo = Vector<float, 7>::ZeroVector;
| ^
<source>:13:29: note: incomplete type 'const Vector<float, 7>' is not a literal type
13 | static constexpr Vector ZeroVector{};
| ^
<source>:5:7: note: definition of 'Vector<float, 7>' is not complete until the closing '}'
5 | class Vector
| ^
1 error generated.
Compiler returned: 1
MSVC gives the following error:
<source>(13): error C2027: use of undefined type 'Vector<float,7>'
<source>(5): note: see declaration of 'Vector<float,7>'
<source>(13): note: the template instantiation context (the oldest one first) is
<source>(18): note: see reference to class template instantiation 'Vector<float,7>' being compiled
Compiler returned: 2
When I change constexpr
to const constinit
for ZeroVector
, this compiles on all three major compilers when the definition is moved outside the class like so:
template<class T, size_t N> requires std::is_arithmetic_v<T> && (N >= 1)
const constinit Vector<T, N> Vector<T, N>::ZeroVector{};
So why does constexpr
compile only on GCC and const constinit
compiles on all three major compilers?
This is an old gcc bug and the program is ill-formed.
When using constexpr
or inline
with the declaration of a static data member, the type must be a complete type. But Vector<T,N>
is not complete at the point of the static data member's definition/declaration.
From static data member:
However, if the declaration uses constexpr or inline (since C++17) specifier, the member must be declared to have complete type.
Here is the old gcc bug report: