【问题标题】:Unions in C++11: default constructor seems to be deletedC++11 中的联合:默认构造函数似乎已被删除
【发布时间】:2016-01-03 20:43:35
【问题描述】:

我试图了解 C++11 是如何扩展联合的。改变的一件事是现在能够使用具有非平凡特殊成员函数的非静态数据成员。来自cppreference.com

如果联合包含具有非平凡特殊成员函数(默认构造函数、复制/移动构造函数、复制/移动赋值或析构函数)的非静态数据成员,则默认情况下该函数在联合中被删除并需要由程序员明确定义。 最多一个数据成员可以有一个默认的成员初始化器。

我正在尝试以下代码:

struct X
{
    ~X() {};
};

union U
{
    X x;
    ~U() {};
};

int main()
{
    U s1{};  // works, probably aggregate initialization
    U s2;    // DOES NOT compile, why?
}

Live on Coliru

这里X(用作联合的数据成员)有一个用户提供的析构函数,因此联合的析构函数默认被删除。因此,我明确提供了一个。但是代码编译失败,报错

注意:'U::U()' 被隐式删除,因为默认定义格式不正确:

如果我删除最后一行U s2;,代码将编译。

问题这是怎么回事?为什么U s1{}; 编译,但U s2; 不编译?联合的默认 ctor 是否标记为已删除(如果是,为什么?!),在第一种情况下,我们只是聚合初始化?请注意,如果我提供U(){}; // not U() = default;,则代码会编译(但如果我只提供X 的ctor,则不会)。

编辑

深挖标准(N4527)后:

工会:9.5/2 [class.union]

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

这似乎是一个 gcc 错误(现已报告 here)。该代码在 clang 和 gcc 4.8.2 或更早版本上编译,它在 gcc4.9 及更高版本上中断(感谢@T.C. 指出)。

编译器:g++5.3,-std=c++11 使用。

【问题讨论】:

  • Clang 对这段代码非常满意。实际上,这对我来说似乎是一个 GCC 错误。
  • @T.C.是的,我实际上是在您发表评论后才看到的。第一感觉clang也是拒绝的,可能是测试了一些修改版。编辑了问题的最后一行,谢谢。
  • @T.C.我将尝试深入研究标准...
  • GCC 4.8.2 上工作,4.9 休息。

标签: c++ c++11 language-lawyer unions


【解决方案1】:

cppreference 引用不清楚。发生的情况是,如果联合的 ANY 成员定义了那些非平凡的特殊成员函数中的 ANY,那么它们中的 ALL 将被删除联合中的默认值。

因此,由于您对 X 有一个重要的析构函数,因此删除了 U 默认构造函数。

【讨论】:

  • 谢谢,如果是这样,那么它就很有意义了。 U s1{}; 之所以有效,是因为我们使用聚合初始化,对吧?
  • 大多数情况下,是的。 C++11 规则允许您在联合中拥有更复杂的类型,但您必须根据您在管理联合的对象/函数中使用的任何类型标记手动管理调用它们的构造函数和析构函数。通常,这意味着使用放置新/删除。
  • 你有报价吗?我不认为这是标准所说的。
  • 联合:9.5/2 "[注意:如果联合的任何非静态数据成员具有非平凡的默认构造函数 (12.1),则复制构造函数(12.8)、移动构造函数(12.8)、复制赋值运算符(12.8)、移动赋值运算符(12.8)或析构函数(12.4),联合的对应成员函数必须是用户提供的,否则会被隐式删除(8.4 .3) 用于联合。--endnote]" 所以似乎只有特定功能被删除,而不是全部。可能是 gcc 错误。
  • @vsoftco 我想,是零初始化,见 [dcl.init] 8.5\8.2
【解决方案2】:

X 不是 pod 类型,因为它具有析构函数,因此无法轻松复制 U 也不是 pod 类型。

U s2; 尝试调用被删除的默认构造函数所以报错

U s1 {}; 使用成员明智的初始化,不要调用任何构造函数

在与非 pod 成员的联合中,联合的默认构造函数被删除,因为它会调用成员的默认构造函数,即编译器不知道哪个成员调用默认构造函数

 Union XX{
   string m1; 
   vector <int> m2;
}

XX 的默认构造函数不能调用 m1 AND m2 的默认构造函数所以被删除了

【讨论】:

  • 同意你的说法,但是我不明白为什么要删除默认构造函数。
  • @vsoftco 我添加了一些我认为它出现的解释
  • 可能是这样,尽管我看不出编译器为什么不能只调用第一个成员的构造函数。和XX x{"test"};一样,编译器调用m1的ctor,即第一个成员的ctor。
  • 根本问题是一次只有一个成员可以安全地处于“已构建”状态,而您必须知道是哪个成员才能安全地破坏联合。我猜他们本可以默认构造一个元素,但他们无法判断哪个元素(如果有)是构造的“正确元素” 通常一个糟糕的默认值可能比没有默认值更糟糕,因为它没有促使人们思考事情。
猜你喜欢
  • 2019-05-07
  • 1970-01-01
  • 2022-01-22
  • 1970-01-01
  • 2014-12-21
  • 2013-08-19
  • 2018-10-28
  • 1970-01-01
  • 2022-11-12
相关资源
最近更新 更多