Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

`default` constructor with uninitialized member in constant expression

Tags:

c++

constexpr

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

like image 346
Human-Compiler Avatar asked Jul 13 '21 17:07

Human-Compiler


People also ask

Does default constructor initialize members?

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.

Is constexpr a default constructor?

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.

Can a constructor be constexpr?

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.

Is constexpr a const?

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 .


Video Answer


1 Answers

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.

like image 55
Brian Bi Avatar answered Oct 25 '22 17:10

Brian Bi