C++ 14 constexpr在构造函数中的条件初始化

csh*_*ton 8 c++ unions constexpr c++14

我想根据参数选择在构造函数中初始化的union成员.以下是一个有效的示例:

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

虽然我使用intfloat,我们的目标是与其他更复杂的类型(但仍允许在C++ 14集),因此,使用位置,新的(不是分配)的工作.

问题是这个构造函数不能constexprconstexpr方法那样不允许使用placement-new .有没有办法解决这个问题(除了使isint参数成为正式类型系统的一部分)?某种类型的条件初始化列表可以工作,但我不知道有办法做到这一点.

Ric*_*ith 7

有一个技巧.关键部分是:

  1. 联合的默认副本或移动构造函数复制对象表示(从而复制活动成员),并且在常量表达式求值中是允许的.
  2. 初始化完成后,您无法在常量表达式求值中更改活动联合成员,但您可以委托另一个构造函数来延迟选择.
  3. 如果委托复制或移动构造函数,则可以传入另一个已初始化为正确状态的对象并复制其活动成员.

把它们放在一起,我们得到:

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'
Run Code Online (Sandbox Code Playgroud)

这是最近的Clang,GCC和EDG所接受的,并且只需要C++ 11 constexpr.

警告: GCC 5.1.0有一个错误,它错误地编译了上面的代码(初始化ab0); GCC的早期版本或更高版本中不存在此错误.