【问题标题】:How could the exception specifier on move assignment operator affect that of move constructor?移动赋值运算符的异常说明符如何影响移动构造函数的异常说明符?
【发布时间】:2016-01-08 12:59:34
【问题描述】:

我正在使用 GCC 5.2 和 clang 3.6 进行测试,两者都在 C++14 模式下,它们给出了相同的输出。

如下代码

#include <iostream>
#include <type_traits>

struct S {
  // S& operator= (S&&) noexcept { return *this; }
};


int main() {
  std::cout << std::is_nothrow_move_constructible<S>::value
            << std::is_nothrow_move_assignable<S>::value;  
}

得到11的结果。但如果取消注释移动赋值运算符,输出变为01。移动赋值运算符的显式 noexcept 规范如何可能影响移动构造函数的规范?

【问题讨论】:

    标签: c++ exception language-lawyer c++14 noexcept


    【解决方案1】:

    通过定义移动赋值运算符,由于rule of 5,您禁用了移动构造函数。该类不是is_nothrow_move_constructible,因为它根本不可移动构造,除非您定义它,否则该构造函数不再可用。

    §12.8 复制和移动类对象

    如果X 类的定义没有显式声明移动构造函数,当且仅当
    X 没有用户声明的复制构造函数,
    X 没有用户声明的复制赋值运算符,
    X 没有用户声明的移动赋值运算符,
    X 没有用户声明的析构函数,并且
    — 移动构造函数不会被隐式定义为已删除。

    在您没有用户定义的移动构造函数的情况下,两者都被隐式定义并遵循以下规范。

    §15.4 例外规范

    隐式声明的特殊成员函数应具有异常规范。如果f 是隐式声明的默认构造函数、复制构造函数、移动构造函数、析构函数、复制赋值运算符或移动赋值运算符,则其隐式异常规范指定类型ID T 当且仅当T 被允许通过f 的隐式定义直接调用的函数的异常规范;如果f 直接调用的任何函数都允许所有异常,则f 应允许所有异常,而f 如果其直接调用的每个函数均不允许异常,则应允许所有异常

    【讨论】:

      【解决方案2】:

      通过声明移动赋值,您已经失去了隐式移动构造函数。
      请参阅下面的完整图表。

      【讨论】:

      • 漂亮的图表!顺便说一句,为什么那些默认字段是暗红色的?
      • 因为它非常危险。
      • @LogicStuff 红色方块表示从 C++98/03 开始​​的已弃用行为。在较新的标准中,我相信它们会“未声明”,但我不太确定是否有保证。在我的 clang 版本中,它们似乎没有声明。
      • 这是一个涉及 Howard Hinnant 的帖子,而不是制作这张桌子的人:stackoverflow.com/questions/24342941/… Howard 的演讲:accu.org/content/conf2014/Howard_Hinnant_Accu_2014.pdf
      • 漂亮的图表!感谢您的链接。红色表示在 C++11 及更高版本中已弃用。它们尚未被删除,但它们可能会成为未来的标准。指南:不要依赖于已弃用的行为。如果显式声明析构函数、复制构造函数或复制赋值中的任何一个,则显式声明复制构造函数和复制赋值。
      【解决方案3】:

      在这种情况下根本不会生成移动构造函数 - 它与 noexcept 无关。

      来自 cppreference:

      如果没有为类类型(结构、类或联合)提供用户定义的移动构造函数,并且以下所有情况都为真:

      • 没有用户声明的复制构造函数
      • 没有用户声明的复制赋值运算符
      • 没有用户声明的移动赋值运算符
      • 没有用户声明的析构函数 (直到 C++14)

      由于下一节中详述的条件,隐式声明的移动构造函数未定义为已删除 然后编译器将声明一个移动构造函数作为其类的非显式内联公共成员,并带有签名 T::T(T&&)。

      【讨论】:

        【解决方案4】:

        12.8/9:

        如果X 类的定义没有显式声明移动构造函数,当且仅当

        • X 没有用户声明的复制构造函数,

        • X 没有用户声明的复制赋值运算符,

        • X 没有用户声明的移动赋值运算符,并且

        • X 没有用户声明的析构函数。

        通过声明移动赋值运算符,您可以完全防止类具有任何移动构造函数。

        【讨论】:

          【解决方案5】:

          通过定义移动操作符,您抑制了隐式移动构造函数。这就是std::is_nothrow_move_constructible 失败的原因。提供它以获得所需的输出:

          struct S {
            S(S&&) noexcept {}
            S& operator= (S&&) noexcept { return *this; }
          };
          

          【讨论】:

            猜你喜欢
            • 2016-05-19
            • 2017-05-15
            • 1970-01-01
            • 2020-04-09
            • 1970-01-01
            • 1970-01-01
            • 2020-09-06
            • 1970-01-01
            • 2013-06-15
            相关资源
            最近更新 更多