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];
| ^~~~~~
Live Example
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
Live Example
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?
Live Example
Default constructors are one of the special member functions. If no constructors are declared in a class, the compiler provides an implicit inline default constructor. If you rely on an implicit default constructor, be sure to initialize members in the class definition, as shown in the previous example.
If that user-defined default constructor would satisfy the requirements of a constexpr constructor, the implicitly defined default constructor is a constexpr constructor. A constexpr constructor is implicitly inline.
A constructor that is declared with a constexpr specifier is a constexpr constructor. Previously, only expressions of built-in types could be valid constant expressions. With constexpr constructors, objects of user-defined types can be included in valid constant expressions.
A constexpr variable must be initialized at compile time. All constexpr variables are const . A variable can be declared with constexpr , when it has a literal type and is initialized. If the initialization is performed by a constructor, the constructor must be declared as constexpr .
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With