3

我正在使用 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规范如何可能影响移动构造函数的规范?

4

5 回答 5

7

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

§12.8 复制和移动类对象

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

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

§15.4 例外规范

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

于 2015-10-10T18:44:04.447 回答
5

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

在此处输入图像描述

于 2015-10-10T18:50:06.073 回答
2

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

来自 cppreference:

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

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

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

于 2015-10-10T18:43:21.250 回答
2

12.8/9:

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

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

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

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

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

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

于 2015-10-10T18:44:01.460 回答
1

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

struct S {
  S(S&&) noexcept {}
  S& operator= (S&&) noexcept { return *this; }
};
于 2015-10-10T18:43:03.207 回答