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

C++14 constexpr union conditional initialization in constructor

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

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

当我使用 intfloat 时,目标是使用其他更复杂的类型(但在 C++14 联合中仍然允许),因此使用放置-新的(而不是作业)。

问题是此构造函数不能 constexpr,因为 constexpr 方法中不允许放置新的。有什么办法解决这个问题(除了使 isint 参数成为正式类型系统的一部分之外)?某种类型的条件初始化器列表可以工作,但我不知道有什么方法可以做到。

有个窍门。关键部分是:

  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'

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

警告: GCC 5.1.0 存在错误编译上述代码的错误(将 ab 初始化为 0) ;此错误不存在于早期或更高版本的 GCC 中。

对于简单的可构造对象,不需要new。您可以简单地通过赋值运算符开始对象生命周期和 select 活动 union 成员。

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

如果成员内部某处真的有一个构造函数,那么有必要使用理查德的答案。