繁体   English   中英

使用值而不是引用来对赋值运算符进行分支

[英]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的语言后果是什么?

特别:

  1. 根据规范,这是否算作T的复制赋值运算符?
  2. 根据规范,这是否算作T的移动赋值运算符?
  3. T会有编译器生成的复制赋值运算符吗?
  4. T会有编译器生成的移动赋值运算符吗?
  5. 这对std::is_move_assignable等特征类std::is_move_assignable

其中大部分内容在§12.8中描述。 第17段定义了什么算作用户声明的副本赋值运算符:

用户声明的复制赋值运算符X::operator=是类X的非静态非模板成员函数,其中只有一个参数类型为XX&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_assignablestd::is_move_assignable在表49中描述为具有相同的值,分别是is_assignable<T&,T const&>::valueis_assignable<T&,T&&>::value 该表告诉我们is_assignable<T,U>::value在以下时为true

表达式declval<T>() = declval<U>()在被视为未评估的操作数时是格式良好的(第5条)。 执行访问检查,就像在与TU无关的上下文中一样。 仅考虑赋值表达式的直接上下文的有效性。

由于declval<T&>() = declval<T const&>()declval<T&>() = declval<T&&>()对于该类是格式良好的,因此它仍然可以作为可分配的副本并移动可分配。

正如我在评论中提到的,对于所有这一切的好奇是,在存在构造函数的情况下, operator=将正确执行移动,但从技术上讲,不算作移动赋值运算符。 如果类没有复制构造函数,那就更奇怪了:它将有一个副本赋值运算符,它不会复制,只会移动。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM