使用值而不是引用的赋值运算符的分支
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
这样的特质类的
大部分内容在§12.8中进行了描述。第17段定义了什么是用户声明的副本分配运算符:
用户声明的复制分配运算符
X::operator=
是类X
的非静态非模板成员函数,仅具有一个类型为X
、X&
、const X&
、volatile X&
或const volatile X&
的参数。
第19段定义了什么算是用户声明的移动分配运算符:
用户声明的移动分配运算符
X::operator=
是非静态的类CCD_ 19的非模板成员函数键入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条)。访问检查执行为如果在与CCD_ 31和CCD_。只有考虑赋值表达式的直接上下文。
由于declval<T&>() = declval<T const&>()
和declval<T&>() = declval<T&&>()
都是该类的良好形式,因此它仍然算作可复制可分配和可移动可分配。
正如我在评论中提到的,这一切令人好奇的是,在移动构造函数存在的情况下,operator=
将正确地执行移动,但从技术上讲,不算移动赋值运算符。如果类没有复制构造函数,那就更奇怪了:它将有一个复制赋值运算符,它不进行复制,只进行移动。
- 标准库类型的赋值运算符的引用限定符
- 移动赋值运算符;尝试引用已删除的函数.我该如何解决这个问题?
- 为什么我需要三个嵌套的大括号来调用赋值运算符,将const引用到二维数组
- 如果类在 C++ 中具有常量或引用类型的非静态数据成员,为什么编译器不提供默认赋值运算符?
- 为什么类的赋值运算符的返回类型通常是非常量(而不是常量)引用?
- 引用模板类型的赋值运算符需要非常量重载
- 为什么在取消引用的指向接口的指针上使用赋值运算符不是编译器错误
- 如何为具有自引用指针的类实现复制构造函数/赋值运算符
- 尽管返回了引用,但无法链接重载的赋值运算符
- 错误:非静态引用成员"std::ostream&Student::out",无法使用默认赋值运算符
- 我应该为赋值运算符使用左值引用限定符吗
- 错误:非静态引用成员,无法使用默认赋值运算符
- 带有右值引用参数的模板赋值运算符与 vs2013 和 gcc 的行为不同
- 如果类具有引用数据成员,为什么编译器不合成默认赋值运算符
- 为什么在赋值运算符重载中返回引用
- 为什么引用类型成员会导致删除隐式声明的复制赋值运算符
- 类成员作为对另一个的引用:赋值运算符中的崩溃
- 使用值而不是引用的赋值运算符的分支
- 带有赋值运算符的右值引用
- 处理赋值运算符重载;您可以重新分配引用吗?