为什么接受默认的noexcept移动构造函数?

假设以下 c++17 代码:

#include <type_traits>
namespace dtl
{
   struct One
   {
      explicit One(int);
      ~One() = default;
      One(const One &) = delete;
      auto operator=(const One &) -> One & = delete;
      auto operator=(One &&) -> One & = delete;
      One(One &&); // Throwable, not default;
      int m_int;
   };
   struct Two 
   {
      explicit Two(int);
      ~Two() = default;
      Two(const Two &) = delete;
      auto operator=(const Two &) -> Two & = delete;
      auto operator=(Two &&) noexcept -> Two & = delete;
      Two(Two &&) noexcept = default;
      One m_one;
   };
   One::One(One &&) { throw 1; }

   static_assert(std::is_nothrow_move_constructible_v<Two>);
}

编译器资源管理器中的代码

在这里我们清楚地看到类的移动构造函数One没有被标记为 noexcept。类Two有一个默认的移动构造函数,它被明确要求为 noexcept。

如果我们检查这段代码,它会使用 GCC 主干、Clang 主干、MSVC 19.28 进行编译,但会以 MSVC19.24 失败。

我检查了以下来源,这些来源似乎告诉我Two需要删除移动构造函数:

  • 带有“noexcept”构造函数的程序被 gcc 接受,被 clang 拒绝
  • 'noexcept = default' 编译错误
  • 错误 35204 - 显式默认默认构造函数的 std::chrono 异常规范与计算出的不匹配

CWG 问题 1778阅读(N4296 [dcl.fct.def.default]/p3):

如果使用与隐式声明上的异常规范不兼容 (15.4) 的异常规范声明显式默认的函数,则

如果该函数在其第一次声明时明确默认,则将其定义为已删除;否则,程序格式错误。

基于这些信息,我只能得出结论,所有 3 个编译器都认为Twono_throw_move_constructible是错误的,并且应该隐式删除移动构造函数。因为这三个人都忽略了这个标准很奇怪,所以我想知道:这真的是一个编译器错误还是我遗漏了什么。

回答

我相信您正在查看过时的信息。DR1778 已被P1286R2取代。如果您查看实施状态,您将看到 gcc 10 和 clang 9 实施了这个新决议。

事实上,如果你回到 godbolt 中旧的 gcc 版本,它会告诉你:

<source>: In function 'int main()':
<source>:35:25: error: use of deleted function 'dtl::Two::Two(dtl::Two&&)'
   35 |     auto b = std::move(a);
      |                         ^
<source>:23:7: note: 'dtl::Two::Two(dtl::Two&&) noexcept' is implicitly deleted because its exception-specification does not match the implicit exception-specification ''
   23 |       Two(Two &&) noexcept = default;
      |       ^~~
Compiler returned: 1
<source>: In function 'int main()':
<source>:35:25: error: use of deleted function 'dtl::Two::Two(dtl::Two&&)'
   35 |     auto b = std::move(a);
      |                         ^
<source>:23:7: note: 'dtl::Two::Two(dtl::Two&&) noexcept' is implicitly deleted because its exception-specification does not match the implicit exception-specification ''
   23 |       Two(Two &&) noexcept = default;
      |       ^~~
Compiler returned: 1

您可以在此处找到 gcc 讨论。根据这份清单,P1286R2 被接受为 DR,这意味着它可以追溯适用于以前的标准。因此,较新的编译器将以您注意到的方式运行,独立于所选的 C++ 标准。

但是,在运行时,这将按预期失败:

[:~/tmp] $ /usr/local/Cellar/llvm/11.0.0/bin/clang++ -std=c++17 mv.cpp  && ./a.out
libc++abi.dylib: terminating with uncaught exception of type int
Abort trap: 6


以上是为什么接受默认的noexcept移动构造函数?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>