noexcept 对显式默认的移动构造函数/赋值运算符有影响吗?

Ale*_*104 10 c++ move-semantics noexcept

class C
{
    public:
        C(C&&) = default; // (1)
        C& operator=(C&&) = default; // (1)

        C(C&&) noexcept = default; // (2)
        C& operator=(C&&) noexcept = default; // (2)
}
Run Code Online (Sandbox Code Playgroud)

据我所知,如果移动构造函数/赋值运算符是由用户隐式生成或显式默认的(1),编译器将noexcept根据类的所有成员是否noexcept为移动操作提供保证来决定是否应该使用这些特殊成员函数。但是,如果我想使用默认移动特殊成员并强制它们不noexcept考虑移动操作的底层成员异常保证,该怎么办?(1)和声明之间的编译器有什么区别吗(2)

Ale*_*104 10

好吧,我在 Nico 的 Josuttis 书《C++ Move Semantics - The Complete Guide》中找到了答案:

\n
\n

当您有默认的特殊成员函数时,您可以显式指定\n与生成的保证不同的 noexcept 保证。例如\n:

\n
class C\n{\n  ...\n public:\n  C(const C&) noexcept = default;     // guarantees not to throw (OK since C++20)\n  C(C&&) noexcept(false) = default;   // specifies that it might throw (OK since C++20)\n  ...\n};\n
Run Code Online (Sandbox Code Playgroud)\n

在 C++20 之前,如果生成的 noexcept 条件与指定的 noexcept 条件相矛盾,则定义的函数将被删除。

\n
\n

我问这个问题是因为我希望 STL 算法始终将移动语义应用于我的类,无论其成员的异常保证如何,并且我准备好处理std::abort出现问题的情况。但似乎在 C++ 20 之前,强制保证noexcept移动特殊成员函数的唯一方法是显式定义它们,即使您不需要自己的实现。事实证明,C++20 通过允许我们noexcept为默认的特殊成员函数指定保证来解决这个问题。当然,与任何其他noexcept函数一样,如果类的任何成员在移动操作期间抛出异常,std::abort如果为该类声明了移动构造函数/赋值运算符,则将调用该类,正如@Micha\noexcept所指出的那样xc3\xablRoy在评论部分。

\n

  • 也许在其他阅读此问答的人的答案中提及此行为。 (5认同)
  • 我的意思是“noexcept(noexcept(X::X(X&&) && ...)”,抱歉打错字了。 (3认同)
  • 谢谢你挖掘这个 (2认同)
  • @阿列克谢104。这意味着对于移动构造函数,默认的 noexcept 条件等于 `noexcept(noexcept(X::X(X&&)...);`,其中 `X...` 是类成员的类型。因此,如果您指定 noexcept,并且其中一个数据成员的构造函数抛出异常,您的程序将执行硬退出。这在您的答案中绝对值得一提,因为在 c++20 之前,这种行为是不可能的。 (2认同)
  • @MichaëlRoy >`“因此,如果您指定 noexcept,并且其中一个数据成员的构造函数抛出异常,您的程序将执行硬退出。”` 是的,我理解这一点,并且我有意想要这种行为。我希望 STL 算法始终将移动语义应用于我的类,无论其成员的异常保证如何,并且如果出现问题,我接受“std::abort”。谢谢你的澄清。 (2认同)