不受限制的联合是否需要放置新的和构造函数定义?

Do unrestricted unions require placement new and a constructor definition?

我看到的unrestricted union的例子在构造的时候好像总是用placement new。有关 C++11 功能的维基百科文章在联合的构造函数中使用了放置 new。

https://en.wikipedia.org/wiki/C%2B%2B11#Unrestricted_unions

#include <new> // Required for placement 'new'.

struct Point {
    Point() {}
    Point(int x, int y): x_(x), y_(y) {} 
    int x_, y_;
};

union U {
    int z;
    double w;
    Point p; // Illegal in C++03; legal in C++11.
    U() {new(&p) Point();} // Due to the Point member, a constructor definition is now required.
};

这里有必要使用placement new吗?例如,这段代码使用 gcc 和 valgrind 编译时没有警告,当联合用于保存字符串时没有显示内存泄漏:

struct HasUnresUnion
{
    enum { Int, String } tag;

    HasUnresUnion(int i)
      : tag(Int),
        as_int(i)
    {}

    HasUnresUnion(std::string str)
      : tag(String),
        as_str(std::move(str))
    {}

    ~HasUnresUnion()
    {
        using std::string;
        if (tag == String)
            as_str.~string();
    }

    union
    {
        int as_int;
        std::string as_str;
    };
};

这里似乎没有任何歧义,所以我不明白为什么标准会禁止这样做。这是法典吗?当联合未初始化(而不是被分配给)时,是否需要放置新的?联合中的构造函数是否需要?我肯定见过没有自己的构造函数的不受限制的联合,但维基百科明确指出它是必需的。

不,这里不需要放置新的。该标准规定,在不受限制的联合的情况下,应显式调用字段构造函数,否则字段将未初始化。

您可以使用传统方式调用构造函数

U(): p() {}

异域风情

U() { new(&p) Point(); }

如果在调用 U.

的构造函数之前无法构造字段,则第二个变体可能会有用

在这种情况下也不要忘记放置析构函数。

简介

您显示的代码段非常安全;在初始化 类联合 class.

时,法律允许您初始化 one 非静态数据成员

例子

维基百科文章有一个示例,其中使用了 placement-new,因为他们在可以直接初始化某个成员的点之后写入成员。

union A {
   A () { new (&s1) std::string ("hello world"); }
  ~A () { s1.~basic_string<char> (); }
  int         n1;
  std::string s1;
};

然而,前面的片段在语义上等同于以下片段,其中我们明确声明 A::s1 应在构造 A.

时初始化
union A {
   A () : s1 ("hello world") { }
  ~A () { s1.~basic_string<char> (); }
  int         n1;
  std::string s1;
};

详细说明

在您的代码段中,您的 class 中有一个 匿名联合 (这使您的 class 成为一个 类联合 class),这意味着除非你在 class 的初始化期间初始化 union 一个 成员——你必须使用placement-new 稍后初始化它们。


标准怎么说?

9.5/1 -- <b>Unions</b> -- [class.union]p1

In a union, at most one of the non-static data members can be active at any time, that is, the value of at most one of the non-static data members can be stored in a union at any time.

3.8/1 -- <b>Object lifetime</b> -- [basic.life]p1

[...]

The lifetime of an object of type T begins when:

  • storage with the proper alignment and size for type T is obtained, and
  • if the object has non-trivial initialization, its initialization is complete.

The lifetime of an object of type T ends when:

  • if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or
  • the storage which the object occupies is reused or released.