5
votes

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
}
2
Instead of a C-style array you could use a std::vector which doesn't have to initialize all objects at once. It is also possible to make A a = t.initializeA(); work, if you modify the other class. Now you are just fighting the language. - Bo Persson

2 Answers

4
votes

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.

1
votes

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.