就像我有这样的结构:
struct S
{
S(const S &arg) : (arg.bIsDouble ? v1{arg.v1} : v{arg.v}) {}
bool bIsDouble{false};
union {
vector<int> v;
double v1;
};
} ;
如何让复制构造函数初始化&#39; v&#39;或者&#39; v1&#39;根据某些条件?
答案 0 :(得分:7)
我会将您的工作外包给Boost.Variant:
struct S {
S(const S&) = default;
boost::variant<double, std::vector<int> > v;
};
如果您不想使用Boost,您可以阅读有关如何编写受歧视的联合here,然后实现自己的联盟。
因为你只需要两种类型,这不是太复杂,虽然它仍然非常容易出错并涉及很多代码:
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;
};
答案 1 :(得分:4)
构造函数初始化列表在这里没有帮助。
您必须在类构造函数中使用placement new,然后在析构函数中销毁(通过手动调用析构函数)正确的成员。此外,由于您定义了析构函数,因此您应该定义或删除The Big Five的其余部分。
最小代码:
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.
};
请注意,在类型之间切换更难:如果您使用了vector
,现在想要使用double
,则需要首先销毁vector
。我建议隐藏访问器函数后面的数据以强制执行不变量。
...或者只使用boost::variant
。它更简单,更容易出错。