Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trivial default constructor can't be constexpr?

It looks like the C++ standard currently prevents trivial default constructors from being constexpr (if there are any non-static member variables), because a trivial default constructor must do nothing, but a constexpr constructor must initialize everything. This, unfortunately, prevents the type from being a POD type.

Is there any workaround that would allow a class type to have a default constructor that can be used in constexpr code without making the class non-POD?

I could work around it by adding a redundant parameter to the constructor so that constexpr code could use a non-default constructor, but that seems rather dirty, especially because in the cases I care about, the initialized values are unnecessary and will all be written-over in later code, so I'd prefer a trivial default constructor. For example:

struct A {
    int a;

    // Non-default constructors necessitate an explicit default
    // constructor if default constructor is to exist
    constexpr A(int b) : a(b) {}

#if 1 // non-POD
    // constexpr default constructor must initialize everything,
    // but this makes A non-POD.
    constexpr A() : a(0) {}
#else // Workaround
    A() = default;
    struct UnnecessaryClass {};
    constexpr A(UnnecessaryClass) : a(0) {}
#endif
};
constexpr void someConstexprFunction(ClassThatADoesntKnowAbout& t) {
#if 1 // non-POD
    A a;             // I'd prefer this not be initialized yet,
#else // Workaround
    A a(A::UnnecessaryClass());
#endif
    t.initializeA(a) // because it's initialized here.
    // It doesn't make a difference when it's run at compile-time,
    // but the redundant initialization is rather unfortunate if it's
    // done at run-time.
}
void someNonConstexprFunction() {
    A *pa = new A[1000000]; // array is redundantly initialized in non-POD case
    ... // Other downsides of non-POD types, too
}
like image 613
Neil Dickson Avatar asked Sep 10 '17 01:09

Neil Dickson


2 Answers

Update: Trivial default initialization can be constexpr since C++20: wg21.link/P1331R2

This paper proposes permitting default initialization for trivially default constructible types in constexpr contexts while continuing to disallow the invocation of undefined behavior. In short, so long as uninitialized values are not read from, such states should be permitted in constexpr in both heap and stack allocated scenarios.


A Trivial constructor can't be constexpr because the result of initialising with a trivial constructor is an uninitialised value.

Illustration: What should the ::value of AVal be in this case?

struct A { int a; }

constexpr A a;
using AVal = std::integral_constant<int, a.a>;

There's no answer.

like image 60
Richard Hodges Avatar answered Oct 23 '22 03:10

Richard Hodges


Not only can't you use a trivial constructor in a constexpr context, you can't trivially construct built-in types, either.

constexpr evaluation implements a limited subset of C++, which doesn't include uninitialized values. So they are forbidden.

The problem may not be that it's difficult to get a POD class, but that PODs don't do what you want.

like image 43
Potatoswatter Avatar answered Oct 23 '22 05:10

Potatoswatter