[英]Ramification of assignment operators with values instead of references
通常,我们将类型T
副本赋值运算符定义为T& operator=(const T&)
,并将类型T
赋值运算符移动为T& operator=(T&&)
。
但是,当我们使用值参数而不是引用时会发生什么?
class T
{
public:
T& operator=(T t);
};
这应该使T既可以复制也可以移动。 但是,我想知道的是T
的语言后果是什么?
特别:
T
的复制赋值运算符? T
的移动赋值运算符? T
会有编译器生成的复制赋值运算符吗? T
会有编译器生成的移动赋值运算符吗? std::is_move_assignable
等特征类std::is_move_assignable
? 其中大部分内容在§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=
将正确执行移动,但从技术上讲,不算作移动赋值运算符。 如果类没有复制构造函数,那就更奇怪了:它将有一个副本赋值运算符,它不会复制,只会移动。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.