分配给 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;
                                  ^