Nic*_*las 14 c++ language-lawyer move-semantics c++11
通常情况下,我们定义拷贝赋值操作符类型T为T& operator=(const T&)和移动赋值运算符类型T的T& operator=(T&&).
但是,当我们使用值参数而不是引用时会发生什么?
class T
{
public:
T& operator=(T t);
};
Run Code Online (Sandbox Code Playgroud)
这应该使T既可以复制也可以移动.但是,我想知道的是什么是语言后果T?
特别:
T根据规范,这是否算作复制赋值运算符?T根据规范,这是否算作移动赋值运算符?T有一个编译器生成的拷贝赋值运算符?T有一个编译器生成的移动赋值运算符?std::is_move_assignable有何影响?R. *_*des 14
其中大部分内容在§12.8中描述.第17段定义了什么算作用户声明的副本赋值运算符:
用户声明的拷贝赋值运算符
X::operator=是类的非静态的非模板成员函数X与类型的只有一个参数X,X&,const X&,volatile X&,或const volatile X&.
第19段定义了什么算作用户声明的移动赋值运算符:
甲用户声明的举动赋值运算符
X::operator=是类的非静态的非模板成员函数X类型的正好一个参数X&&,const X&&,volatile X&&,或const volatile X&&.
因此,它计为复制赋值运算符,但不是移动赋值运算符.
第18段告诉编译器何时生成复制赋值运算符:
如果类定义未显式声明复制赋值运算符,则会隐式声明一个.如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的复制赋值运算符被定义为已删除; 否则,它被定义为默认值(8.4).如果类具有用户声明的复制构造函数或用户声明的析构函数,则不推荐使用后一种情况.
第20段告诉我们编译器何时生成移动赋值运算符:
如果类X的定义没有显式声明一个移动赋值运算符,那么当且仅当
[...]
- X没有用户声明的复制赋值运算符时,才会隐式声明一个默认运算符,
[...]
由于该类具有用户声明的复制赋值运算符,因此编译器不会生成任何隐式的运算符.
std::is_copy_assignable和std::is_move_assignable列在表49具有相同的值,分别描述is_assignable<T&,T const&>::value和is_assignable<T&,T&&>::value.该表告诉我们的时间is_assignable<T,U>::value是true:
declval<T>() = declval<U>()当作为未评估的操作数处理时,表达式是良好的形式(第5条).执行访问检查就像在与T和无关的上下文中一样U.仅考虑赋值表达式的直接上下文的有效性.
由于两个declval<T&>() = declval<T const&>()与declval<T&>() = declval<T&&>()都能很好地形成为阶级,它仍然被认为拷贝分配和移动分配.
正如我在评论中提到的,对于所有这一切的好奇是,在移动构造函数的存在下,operator=它将正确执行移动,但在技术上不算作移动赋值运算符.如果类没有复制构造函数,那就更奇怪了:它将有一个副本赋值运算符,它不会复制,只会移动.