分配给 deleted/uninitialized 对象
Assignment to deleted/uninitialized object
我有一个class这样的
struct foo {
bool type;
union {
struct { std::string s; };
struct { std::function<std::string(void)> f; };
};
};
我需要定义赋值运算符,但是当我分配不同的类型时,我基本上是在分配给未初始化的字段(我知道我必须先显式调用析构函数)。
我的猜测是这样的赋值是未定义的行为,因为我不知道字符串或函数的赋值是否不会重用某些字段。
如何在不调用任何未定义行为的情况下正确执行此操作?
我仅限于 C++11。
首先,匿名结构在 C++ 中的格式不正确。您可以改用例如:
union {
std::string s;
std::function<std::string(void)> f;
};
My guess is that such assignment is undefined behaviour
正确。
How can i do this correctly without invoking any undefined behaviour?
您可以使用标记联合模式。您似乎已有 type
成员。您可以使用它来指定哪个成员处于活动状态。如果您对该成员有其他计划,只需添加一个新计划即可。
一旦你可以使用结构成员来检查哪个联合成员是活跃的,你就可以销毁活跃成员,然后复制构造一个以前不活跃的联合成员,最后更新 type
以表示新的活跃成员.
也就是说,您可以通过使用标记联合的预先存在的通用实现来节省一些时间。例如 Boost 中有一个。
My guess is that such assignment is undefined behavior, since I don't know if assignment of string or function doesn't reuse some fields.
猜对了!赋值总是有一个前提条件,即左侧实际上是该类型的对象。如果我们在左侧没有对象,则所有投注均无效。
How can I do this correctly without invoking any undefined behavior?
施工!当您没有对象时,获得对象的唯一方法就是创建它。因为我们想在特定位置创建它,所以这是新的位置。
我不确定你为什么要将你的类型包装在一个额外的结构中,你想这样做:
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;
}
您需要重构其中的一些内容以支持移动分配而无需大量重复,但这是粗略的想法。
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;
^
我有一个class这样的
struct foo {
bool type;
union {
struct { std::string s; };
struct { std::function<std::string(void)> f; };
};
};
我需要定义赋值运算符,但是当我分配不同的类型时,我基本上是在分配给未初始化的字段(我知道我必须先显式调用析构函数)。
我的猜测是这样的赋值是未定义的行为,因为我不知道字符串或函数的赋值是否不会重用某些字段。
如何在不调用任何未定义行为的情况下正确执行此操作?
我仅限于 C++11。
首先,匿名结构在 C++ 中的格式不正确。您可以改用例如:
union {
std::string s;
std::function<std::string(void)> f;
};
My guess is that such assignment is undefined behaviour
正确。
How can i do this correctly without invoking any undefined behaviour?
您可以使用标记联合模式。您似乎已有 type
成员。您可以使用它来指定哪个成员处于活动状态。如果您对该成员有其他计划,只需添加一个新计划即可。
一旦你可以使用结构成员来检查哪个联合成员是活跃的,你就可以销毁活跃成员,然后复制构造一个以前不活跃的联合成员,最后更新 type
以表示新的活跃成员.
也就是说,您可以通过使用标记联合的预先存在的通用实现来节省一些时间。例如 Boost 中有一个。
My guess is that such assignment is undefined behavior, since I don't know if assignment of string or function doesn't reuse some fields.
猜对了!赋值总是有一个前提条件,即左侧实际上是该类型的对象。如果我们在左侧没有对象,则所有投注均无效。
How can I do this correctly without invoking any undefined behavior?
施工!当您没有对象时,获得对象的唯一方法就是创建它。因为我们想在特定位置创建它,所以这是新的位置。
我不确定你为什么要将你的类型包装在一个额外的结构中,你想这样做:
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;
}
您需要重构其中的一些内容以支持移动分配而无需大量重复,但这是粗略的想法。
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;
^