Reputation: 11220
The following minimal example is rejected by both Clang and GCC for not initializing the array data-member:
class vector3
{
public:
constexpr vector3() = default;
private:
float m_data[3];
};
constexpr auto vec = vector3{};
Which yields the reasonably straight-forward error:
<source>:4:15: error: explicitly defaulted function 'constexpr vector3::vector3()' cannot be declared 'constexpr' because the implicit declaration is not 'constexpr':
4 | constexpr vector3() = default;
| ^~~~~~~
<source>:6:11: note: defaulted default constructor does not initialize 'float vector3::m_data [3]'
6 | float m_data[3];
| ^~~~~~
The goal with the above code was to ensure that vector3
can be used in constant expressions via value-initialization (e.g. vector3{}
), which will zero-initialize the sub-elements (m_data
).
The error occurs due to the use of the constexpr
keyword, and the fix is simply to remove the keyword and to allow default
to correctly deduce whether this can be used in a constant expression:
class vector3
{
public:
vector3() = default;
private:
float m_data[3];
};
constexpr auto vec = vector3{}; // now works?
Curiously, this actually now works -- and is still able to produce a constant expression, with m_data
being zero-initialized, as visible in the assembly for GCC (Similar exists in Clang, but with XOR instructions):
vec:
.zero 12
My question is: How is it possible that = default
produces a (valid) constexpr
constructor, whereas constexpr ... = default
fails due to it not being valid for constexpr
?
This question appears to affect C++ versions prior to C++20 (C++11 through C++17). Was this changed in C++20?
Upvotes: 5
Views: 697
Reputation: 119069
Yes, it's true that in C++20, the rules were changed so that a constexpr
constructor is no longer required to initialize all non-static members and base class subobjects.
Prior to C++20, we have the interesting situation that your constructor cannot be declared constexpr
, but objects of the vector3
type can still be used in constant expressions, because a default constructor that is explicitly defaulted on its first declaration is not actually called during value-initialization unless it is nontrivial (C++17 [dcl.init]/8.2) and thus the prohibition on calling non-constexpr
functions within a constant expression is not triggered. This is not a compiler bug; it's just a quirk in the language.
Upvotes: 6