why declare constrexpr constructors for classes with non-trivial destructors (e.g. unique_ptr, std::variant)

1.7k views Asked by At

As far as I understand (at least for c++14), a destructor cannot be constexpr if it is not trivial (implicit generated or =default). What is the point of declaring constexpr constructors for structures with non-trivial destructors?

struct X {
  int a_;

  constexpr X(int a) : a_{a} {}

  // constexpr ~X(){}; // Error dtor cannot be marked constexpr
  // ~X(){}; // causes  error at y declaration: temporary of non-literal type ‘X’
             // in a constant expression .
};

template <int N> struct Y {};

int main() {
  Y<X{3}.a_> y; // OK only if the destructor is trivial
  (void)y;
}
// tested with c++14 g++-5.1.0 and clang++ 3.5.0

For instance std::unique_ptr has some constructors constexpr (default and nullptr_t), even though the destructor is obviously explicitly defined (sure it has no effects if the object is nullptr, but doesn't that mean that it still has an explicit-defined destructor in order to check if the object is in an empty state, and as I've seen, even an empty destructor doesn't allow an object to be used in a compile-constant expression)

Another example is the proposal for std::variant: it has almost all the constructors constexpr although the destructor has the signature ~variant() and it has to call get<T_j> *this).T_j::~T_j() with j being index().

What am I missing?

1

There are 1 answers

5
T.C. On BEST ANSWER

constexpr constructors can be used for constant initialization, which, as a form of static initialization, is guaranteed to happen before any dynamic initialization takes place.

For example, given a global std::mutex:

std::mutex mutex;

In a conforming implementation (read: not MSVC), constructors of other objects can safely lock and unlock mutex, because std::mutex's constructor is constexpr.