我在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
规范怎么可能会影响移动构造函数的规范?
答案 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)
答案 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; }
};