实践中不受限制的联合

3XX*_*XX0 5 c++ unions c++11

我对无限制工会及其在实践中的应用有一些疑问。假设我有以下代码:

struct MyStruct
{
    MyStruct(const std::vector<int>& a) : array(a), type(ARRAY)
    {}
    MyStruct(bool b) : boolean(b), type(BOOL)
    {}
    MyStruct(const MyStruct& ms) : type(ms.type)
    {
        if (type == ARRAY)
            new (&array) std::vector<int>(ms.array);
        else
            boolean = ms.boolean;
    }
    MyStruct& operator=(const MyStruct& ms)
    {
        if (&ms != this) {
            if (type == ARRAY)
                array.~vector<int>(); // EDIT(2) 
            if (ms.type == ARRAY)
                new (&array) std::vector<int>(ms.array);
            else
                boolean = ms.boolean;
            type = ms.type;
        }
        return *this;
    }
    ~MyStruct()
    {
        if (type == ARRAY)
            array.~vector<int>();
    }

    union {
        std::vector<int> array;
        bool             boolean;
    };
    enum {ARRAY, BOOL} type;
};
Run Code Online (Sandbox Code Playgroud)
  1. 此代码是否有效:)?
  2. 每次我们使用布尔值时是否有必要显式调用向量析构函数(如此处所述http://cpp11standard.blogspot.com/2012/11/c11-standard-explained-1-unrestricted.html
  3. 为什么需要新的放置位置,而不是仅仅执行'array = ms.array'之类的操作?

编辑:

  • 是的,它可以编译
  • “在匿名联合内部声明的成员实际上是包含类的成员,并且可以在包含类的构造函数中初始化。” (带有非平凡成员的C ++ 11匿名联合
  • 根据建议添加显式析构函数,导致SIGSEV使用g ++ 4.8 / clang 4.2

Ton*_*roy 3

  1. 代码有问题:array.clear();改为array.~vector<int>();

说明:operator=正在new对尚未被破坏的对象使用放置,这可以做任何事情,但实际上您可以预期它会泄漏前一个数组一直在使用的动态内存(clear()不会释放内存/更改容量,它只是破坏元素和变化size)。

从 9.5/2 开始:

如果联合体的任何非静态数据成员具有非平凡的默认构造函数 (12.1)、复制构造函数 (12.8)、移动构造函数 (12.8)、复制赋值运算符 (12.8)、移动赋值运算符 (12.8) 或析构函数 ( 12.4),联合体的相应成员函数必须是用户提供的,否则它将被隐式删除(8.4.3)联合体。

因此,vector构造函数、析构函数等永远不会自行启动:您必须在需要时显式调用它们。

9.5/3中有一个例子:

考虑以下联合:

union U {
    int i;
    float f;
    std::string s;
};
Run Code Online (Sandbox Code Playgroud)

由于 std::string (21.3) 声明了所有特殊成员函数的非平凡版本,因此 U 将具有隐式删除的默认构造函数、复制/移动构造函数、复制/移动赋值运算符和析构函数。要使用 U,这些成员函数中的部分或全部必须由用户提供。

最后一点 - “要使用 U,这些成员函数中的部分或全部必须由用户提供。” - 似乎假设U需要协调其自己模糊的值语义行为,但在您的情况下,周围struct正在这样做,因此您不需要定义任何这些union成员函数。

2:每当数组值被布尔值替换时,我们都必须调用数组析构函数。如果在operator=新数组中放置new而不是分配值,则还必须调用旧数组的析构函数,但当operator=现有内存足以容纳所有要复制的元素时,使用会更有效。基本上,您必须匹配构造和破坏。更新:根据您下面的评论,示例代码有一个错误。

3:为什么需要放置 new 而不是仅仅执行诸如 'array = ms.array' 之类的操作?

array = ms.array 调用std::vector<int>::operator=始终假定this指针指向已正确构造的对象。在该对象内部,您可以预期有一个指针,该指针要么为 NULL,要么引用某些内部短字符串缓冲区,或者引用堆。如果您的对象尚未被破坏,那么operator=很可能会在伪造的指针上调用内存释放函数。Placement new 表示“忽略该对象将占用的内存的当前内容,并从头开始构造一个具有有效成员的新对象。