Bitwize
Bitwize

Reputation: 11220

`default` constructor with uninitialized member in constant expression

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

Upvotes: 5

Views: 697

Answers (1)

Brian Bi
Brian Bi

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

Related Questions