分配给已删除/未初始化的对象

时间:2018-08-22 15:08:58

标签: c++ c++11 initialization destructor unions

我有一堂这样的课

struct foo {
    bool type;
    union {
        struct { std::string s; };
        struct { std::function<std::string(void)> f; };
    };
};

我需要定义赋值运算符,但是当我分配不同的类型时,我基本上是在分配给未初始化的字段(我知道我必须先显式调用析构函数)。

我的猜测是,这种分配是未定义的行为,因为我不知道字符串或函数的分配是否不会重用某些字段。

如何正确执行此操作而不引起任何未定义的行为?

我仅限于C ++ 11。

3 个答案:

答案 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;
                                  ^