移动赋值运算符上的异常说明符如何影响移动构造函数?

时间:2015-10-10 18:33:14

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

我在C ++ 14模式下使用GCC 5.2和clang 3.6进行测试,并且它们提供相同的输出。

以下代码

#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规范怎么可能会影响移动构造函数的规范?

5 个答案:

答案 0 :(得分:7)

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

§12.8复制和移动类对象

  

如果类X的定义没有明确声明一个移动构造函数,那么当且仅当有一个构造函数时,它将被隐式声明为默认值    - X没有用户声明的复制构造函数,
   - X没有用户声明的副本赋值运算符,
   - X没有用户声明的移动分配运算符
   - X没有用户声明的析构函数,以及
   - 移动构造函数不会被隐式定义为已删除。

如果您没有用户定义的移动构造函数,则两者都是隐式定义的,并遵循以下规范。

§15.4例外规范

  

隐式声明的特殊成员函数应具有异常规范。如果f是隐式声明的默认构造函数,复制构造函数,移动构造函数,析构函数,复制赋值运算符或移动赋值运算符,则其隐式异常规范指定类型标识T当且仅当{由T的隐式定义直接调用的函数的异常规范允许{1}};如果它直接调用的任何函数允许所有异常,f将允许所有异常,并且 f如果它直接调用的每个函数都不允许例外,则不允许异常。

答案 1 :(得分:5)

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

enter image description here

答案 2 :(得分:2)

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

来自cppreference:

  

如果没有为类类型(struct,class或union)提供用户定义的移动构造函数,并且满足以下所有条件:

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

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

答案 3 :(得分:2)

12.8 / 9:

  

如果类X的定义未明确声明移动构造函数,则当且仅当

时,将隐式声明一个默认值。      
      
  • X没有用户声明的复制构造函数,

  •   
  • X没有用户声明的副本分配运算符,

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

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

  •   

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

答案 4 :(得分:1)

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

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