C++ 根据c-tor的特定条件选择要初始化的变量?

C++ 根据c-tor的特定条件选择要初始化的变量?,c++,c++11,copy-constructor,c++14,unions,C++,C++11,Copy Constructor,C++14,Unions,如果我有这样的结构: struct S { S(const S &arg) : (arg.bIsDouble ? v1{arg.v1} : v{arg.v}) {} bool bIsDouble{false}; union { vector<int> v; double v1; }; } ; 结构 { S(consts&arg):(arg.bisdoole?v1{arg.v1}:v{arg.v}){ 布尔双{假};

如果我有这样的结构:

struct S
{
   S(const S &arg) : (arg.bIsDouble ? v1{arg.v1} : v{arg.v}) {}

   bool bIsDouble{false};   

   union {
      vector<int> v;

      double v1;
   };
} ;
结构
{
S(consts&arg):(arg.bisdoole?v1{arg.v1}:v{arg.v}){
布尔双{假};
联合{
向量v;
双v1;
};
} ;

如何使复制构造函数根据某些条件初始化“v”或“v1”?

我将您的工作外包给:


构造函数初始化列表在这里没有帮助

必须在类构造函数中使用placement new,然后(通过手动调用析构函数)销毁析构函数中的正确成员。此外,由于您定义了析构函数,因此应该定义或删除大五的其余部分

最小代码:

struct S
{
    bool bIsDouble{false};

    union {
        vector<int> v;
        double v1;
    };

    S(bool d) : bIsDouble(d)
    {
        if(!bIsDouble)
            new(&v) vector<int>();
        // no need to create or destroy a double, since it is trivial
    }

    ~S()
    {
        if(!bIsDouble)
            v.~vector<int>();
    }

    // the other Big Five members are implicitly deleted
    // so no copying, unless you write it yourself.
};
结构
{
布尔双{假};
联合{
向量v;
双v1;
};
S(布尔d):双(d)
{
如果(!双精度)
新的(&v)向量();
//无需创建或销毁double,因为它很琐碎
}
~S()
{
如果(!双精度)
v、 ~vector();
}
//其他五大成员国被含蓄地删除
//所以不要复制,除非你自己写。
};
请注意,在类型之间切换比较困难:如果您曾经使用过
向量
,现在想使用
双重
,则需要先销毁
向量
。我建议将数据隐藏在访问器函数后面,以强制执行不变量


…或者只使用
boost::variant
。它更简单,更不容易出现错误。

我想避免使用Boost库。@FISOCPP然后你可以自己实现,我添加了一个框架你需要在
存储
声明之前添加
alignas()
来支持正确的对齐。@milleniumbug就像我说的,它很复杂而且容易出错,在链接到它的两个不同的库实现之后,我不打算重新发明轮子。@Barry,所以不要。用
std::aligned_storage::type storage替换阵列
那么会调用对象“v”的隐式默认构造函数吗?或者否-因为它是联合体的一部分?如果我正确阅读了标准,在C++11中,联合体的默认构造函数和副本构造函数都将由于
向量
成员而被删除。@MarkB“如果联合体的任何非静态数据成员都有非平凡的默认构造函数(12.1)、复制构造函数(12.8)、移动构造函数(12.8),复制赋值运算符(12.8)、移动赋值运算符(12.8)或析构函数(12.4),则必须由用户提供联合的相应成员函数,否则将隐式删除联合的相应成员函数(8.4.3)。“是的,非常清楚。而
vector
拥有所有这些。请注意,在C++11之前,您的联合是非法的,因为有vector成员。我强烈建议您考虑<代码> Boo::变体< /代码>,而不是试图重新发明已经开发的东西。
struct DoubleOrVector {
    bool is_double;
    static constexpr std::size_t alignment_value = std::max(alignof(double), alignof(std::vector));

    alignas(alignment_value) char storage[std::max(sizeof(double), sizeof(std::vector))];

    DoubleOrVector(double v)
        : is_double(true)
    {
        new (storage) double(v);
    }

    // similar for vector

    DoubleOrVector(const DoubleOrVector& dov) 
        : is_double(dov.is_double)
    {
        if (is_double) {
            new (storage) double(dov.asDouble());
        }
        else {
            new (storage) std::vector<int>(dov.asVector());
        }
    }

    double& asDouble() { 
        assert(is_double);
        return *reinterpret_cast<double*>(storage);
    }

    std::vector<int>& asVector() { 
        assert(!is_double);
        return *reinterpret_cast<std::vector<int>*>(storage);
    }

    // plus other functions here
    // remember to explicitly call ~vector() when we need to
};
struct S {
    S(const S&) = default;
    DoubleOrVector v;
};
struct S
{
    bool bIsDouble{false};

    union {
        vector<int> v;
        double v1;
    };

    S(bool d) : bIsDouble(d)
    {
        if(!bIsDouble)
            new(&v) vector<int>();
        // no need to create or destroy a double, since it is trivial
    }

    ~S()
    {
        if(!bIsDouble)
            v.~vector<int>();
    }

    // the other Big Five members are implicitly deleted
    // so no copying, unless you write it yourself.
};