C ++ 14 conditional initialization constexpr in constructor

I would like to select a union member initialized in the constructor based on the argument. Below is an example that works:

struct A { union { int i; float f; }; A(double d, bool isint) { if (isint) new(&i) int(d); else new(&f) float(d); } }; 

While I use int and float , the goal is to work with other more complex types (but still valid in a C ++ 14 union), hence using place-new (rather than assignment).

The problem is that this constructor cannot be constexpr , because new-placement is not allowed in constexpr methods. Is there any way around this (other than creating part of the isint argument of the formal type system)? Some type of conditional list of initiators will work, but I do not know how to do this.

+7
c ++ unions c ++ 14 constexpr
source share
2 answers

There is a trick. The keys are:

  • The default copy or move constructor for the join copies the representation of the object (and thus copies the active element) and is allowed to evaluate a constant expression.
  • You cannot change the active member of a union in evaluating a constant expression after initialization is complete, but you can delegate to another constructor to defer selection.
  • If you delegate the copy or move constructor, you can transfer another object that is already initialized to the correct state and copy its active element.

Combining this, we get:

 template<typename T> struct tag {}; struct A { union { int i; float f; }; constexpr A(tag<int>, double d) : i(d) {} constexpr A(tag<float>, double d) : f(d) {} constexpr A(double d, bool isint) : A(isint ? A(tag<int>(), d) : A(tag<float>(), d)) {} }; constexpr A a(1.0, true); // ok, initializes 'i' constexpr A b(5, false); // ok, initializes 'f' 

This is accepted by recent Clang, GCC, and EDG and requires only C ++ 11 constexpr .

Warning: GCC 5.1.0 had an error in which it compiled the above code incorrectly (initializing a and b to 0 ); this error is missing in earlier or later versions of GCC.

+6
source share

For trivially constructive objects, there is no need for new . You can start the lifetime of the object and select the active member of the union , simply by the assignment operator.

 struct A { union { int i; float f; }; A(double d, bool isint) { if (isint) i = d; else f = d; } }; 

If there really is some kind of constructor inside the member, then you need to use Richard's answer.

+1
source share

All Articles