【问题标题】:Union's default constructor is implicitly deletedUnion的默认构造函数被隐式删除
【发布时间】:2022-01-22 11:14:39
【问题描述】:

以下代码:

struct non_trivially {
    non_trivially() {};
};

union U {
    bool dummy{false};
    non_trivially value;
};

int main() {
    U tmp;
}

https://godbolt.org/z/1cMsqq9ee

在 clang (13.0.0) 上产生下一个编译器错误:

source>:11:7: error: call to implicitly-deleted default constructor of 'U'
    U tmp;
      ^ <source>:7:19: note: default constructor of 'U' is implicitly deleted because variant field 'value' has a non-trivial default constructor
    non_trivially value;

但使用 MSVC (19.30) 成功编译。

根据 cppreference,它应该是一个有效的代码:https://en.cppreference.com/w/cpp/language/union

如果联合包含具有非平凡默认构造函数的非静态数据成员,则默认删除联合的默认构造函数,除非联合的变体成员具有默认成员初始值设定项。

在我的示例中,U 中有一个带有默认成员初始值设定项的替代方法,因此不应删除默认构造函数,但确实如此。我错过了什么?

【问题讨论】:

  • 问题不在于dummy 的默认成员初始化程序。问题在于value。例如,这可以正常工作(并且功能相同):ideone.com/GadqPM
  • @scohe001 如果要相信 cppreference,我认为这不能回答问题。
  • 当你解决这个问题并进行编译时,你仍然需要非常小心。很难让非平凡的unions 正常工作。通常的建议是使用std::variant,但如果您想了解unions 的秘密方法,这并不是那么有用。
  • 是的,std::variant 很神奇。
  • @user4581301 是的,当然,我在尝试实现变体 lol 时遇到了这种行为

标签: c++ language-lawyer


【解决方案1】:

这是CWG2084。如果您提供默认成员初始化程序,则 Clang 和 gcc 删除默认构造函数是错误的。

在更改被采纳后添加到标准中的相关引用(在[class.default.ctor]中):

如果满足以下条件,则将类 X 的默认默认构造函数定义为已删除:

  • X 是一个联合,它有一个变体成员和一个非平凡的默认构造函数,并且 X 的任何变体成员都没有一个默认成员初始化器,
  • [...]
  • X 是一个联合,它的所有变体成员都是 const 限定类型(或其数组),

(作为唯一两个引用联合类型的点,两者都不适用,因此不应隐式删除)

解决方法是用户提供构造函数:

union U {
    constexpr U() : dummy{false} {}
    bool dummy;
    non_trivially value;
};

// Or this also seems to work
union U {
    constexpr U() {}
    bool dummy{false};
    non_trivially value;
};

【讨论】:

  • 这是一个比我的好多更好的答案,WD。​​span>
  • 是的,我知道解决方法,但用户定义的构造函数并不简单 :( 但是谢谢,您帮助确保这是一个编译器错误。
  • @DenisVorkozhokov 不幸的是,编译器错误意味着你不能有一个带有 this 类型成员的普通构造函数。由于您可能正在使用placement new,alignas(non_trivially) char value_storage[sizeof(non_trivially)]; non_trivially&amp; value() { return *std::launder(&amp;reinterpret_cast&lt;non_trivially&amp;&gt;(value_storage)); } 可能可用
  • placement new 不是 constexpr,仍然不够好 :)
【解决方案2】:

这一切都有些神秘。 gcc 的行为与 clang 相同。

standard 这么说(强调我的):

缺少默认成员初始化器,如果联合的任何非静态数据成员具有非平凡的默认构造函数、复制构造函数、移动构造函数、复制赋值运算符、移动赋值运算符或析构函数, union 对应的成员函数必须是用户提供的,否则会为 union 隐式删除。

但我认为这里的措辞有点模糊。我认为它们的实际意思是您必须为具有(在您的示例中)非平凡构造函数的成员提供一个初始化程序,如下所示:

struct non_trivially {
    non_trivially() {};
};

union U {
    bool dummy;
    non_trivially value { };        // <-- note that I have added an initialiser
};

int main() {
    U tmp;
}

那么,it compiles

【讨论】:

  • 我怀疑这个注释最初是放在“最多一个联合的变体成员可能有一个默认的成员初始化器”的句子之后,或者至少是为了与后面的句子一起阅读。跨度>
  • @Sneftel 也许吧。不过还是有点模糊。
  • @Sneftel Vague 足以愚弄 clang 和 gcc 的人。
猜你喜欢
  • 1970-01-01
  • 2016-09-11
  • 1970-01-01
  • 1970-01-01
  • 2020-11-02
  • 2017-09-10
  • 2015-08-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多