这个问题来自this answer提出的问题。
通常,我们将类型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
等特征类?答案 0 :(得分: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)。后一种情况如果被弃用则弃用 class具有用户声明的复制构造函数或用户声明的 析构函数。
第20段告诉我们编译器何时生成移动赋值运算符:
如果类X的定义没有明确声明移动 赋值运算符,如果是,则将隐式声明为defaultaulted 并且只有在 [...]
- 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
在{:1}}时:
表达式
true
在处理时格式正确 作为未评估的操作数(第5条)。访问检查执行为 如果在与declval<T>() = declval<U>()
和T
无关的上下文中。只有有效期 考虑赋值表达式的直接上下文。
由于U
和declval<T&>() = declval<T const&>()
都适用于该类,因此它仍然可以作为副本分配并移动可分配。
正如我在评论中提到的,对于所有这一切的好奇是,在移动构造函数的存在下,declval<T&>() = declval<T&&>()
将正确执行移动,但技术上不算作移动赋值运算符。如果类没有复制构造函数,那就更奇怪了:它将有一个副本赋值运算符,它不会复制,只会移动。