我有一堂这样的课
struct foo {
bool type;
union {
struct { std::string s; };
struct { std::function<std::string(void)> f; };
};
};
我需要定义赋值运算符,但是当我分配不同的类型时,我基本上是在分配给未初始化的字段(我知道我必须先显式调用析构函数)。
我的猜测是,这种分配是未定义的行为,因为我不知道字符串或函数的分配是否不会重用某些字段。
如何正确执行此操作而不引起任何未定义的行为?
我仅限于C ++ 11。
答案 0 :(得分:2)
我的猜测是,这种分配是未定义的行为,因为我不知道字符串或函数的分配是否不会重用某些字段。
好猜!分配始终以其左侧实际上是该类型的对象为前提。如果我们的左手边没有物体,则所有选择都将关闭。
如何正确执行此操作而不引起任何未定义的行为?
施工!当您没有对象时,获取对象的唯一方法是创建对象。并且由于我们要在特定位置创建它,因此这是新的位置。
我不确定为什么要将类型包装在额外的结构中,您想这样做:
union U {
U() { }
~U() { }
std::string s;
std::function<std::string(void)> f;
} u;
这样,您的赋值运算符将为:
foo& operator=(foo const& rhs) {
if (type) {
if (rhs.type) {
u.s = rhs.u.s;
} else {
u.s.~string();
new (&u.f) std::function<std::string(void)>(rhs.u.f);
}
} else {
if (!rhs.type) {
u.f = rhs.u.f;
} else {
u.f.~function();
new (&u.s) std::string(rhs.u.s);
}
}
return *this;
}
您将需要重构其中的一些以支持移动分配而无需重复,但这是一个粗略的想法。
答案 1 :(得分:0)
首先,匿名结构在C ++中格式错误。您可以改用例如:
union {
std::string s;
std::function<std::string(void)> f;
};
我的猜测是这种分配是不确定的行为
正确。
如何在不调用任何未定义行为的情况下正确执行此操作?
您可以使用标记的联合模式。您似乎已经有一个type
成员。您可以使用它来指定哪个成员处于活动状态。如果您对该成员还有其他计划,只需添加一个新计划即可。
一旦您可以使用struct成员检查哪个联合成员处于活动状态,就可以销毁该活动成员,然后复制构造以前的非活动联合成员,最后更新type
来表示新的活动成员。 / p>
也就是说,您可以使用标记的联合的预先存在的通用实现来节省一些时间。例如,Boost中有一个。
答案 2 :(得分:0)
C ++ 11非常友好,甚至不允许这样做。由于您的联合包含具有非平凡的构造函数和析构函数的字段,因此struct foo
隐式删除了默认构造函数和析构函数。
当我尝试使用以下命令创建struct foo
时,我的旧clang版本3.4.1(带有std = c ++ 11选项)令人窒息:
error: call to implicitly-deleted
和
note: default constructor of 'foo' is implicitly deleted because variant field has a non-trivial default constructor.
如果我尝试使用手动初始化struct foo
(为了避免匿名字段,我稍微更改了foo
):
...
struct foo {
bool type;
union {
struct { std::string s; } s;
struct { std::function<std::string(void)> f; } f;
};
};
int main() {
foo bar { .s = {"a"}};
...
我收到另一个错误(按预期):
error: attempt to use a deleted function
foo bar { .s = {"a"}};
和
note: destructor of 'foo' is implicitly deleted because variant field 's' has a non-trivial destructor
struct { std::string s; } s;
^