用值而不是引用对赋值运算符进行分枝 [英] Ramification of assignment operators with values instead of references
问题描述
此问题来自此答案提出的问题.
This question comes from issues raised by this answer.
通常,我们将类型为T
的副本赋值运算符定义为T& operator=(const T&)
,并将类型为T
的赋值运算符定义为T& operator=(T&&)
.
Normally, we define copy assignment operators for type T
as T& operator=(const T&)
, and move assignment operators for type T
as T& operator=(T&&)
.
但是,当我们使用值参数而不是引用时会发生什么?
However, what happens when we use a value parameter rather than a reference?
class T
{
public:
T& operator=(T t);
};
这应该使T既可以复制又可以移动.但是,我想知道的是T
的语言分支是什么?
This should make T both copy and move assignable. However, what I want to know is what are the language ramifications for T
?
特别是:
- 根据规范,这是否算作
T
的副本分配运算符? - 根据规范,这是否算作
T
的移动分配运算符? -
T
是否具有编译器生成的副本分配运算符? -
T
是否具有编译器生成的移动分配运算符? - 这如何影响像
std::is_move_assignable
这样的特征类?
- Does this count as a copy assignment operator for
T
, according to the specification? - Does this count as a move assignment operator for
T
, according to the specification? - Will
T
have a compiler-generated copy assignment operator? - Will
T
have a compiler-generated move assignment operator? - How does this affect traits classes like
std::is_move_assignable
?
推荐答案
大部分内容在§12.8中进行了描述.第17段定义了什么才算是用户声明的副本分配运算符:
Most of this is described in §12.8. Paragraph 17 defines what counts as user-declared copy assignment operators:
用户声明的副本分配运算符
X::operator=
是类X
的非静态非模板成员函数,具有仅一个类型为X
,X&
,const X&
,volatile X&
的参数,或const volatile X&
.
A user-declared copy assignment operator
X::operator=
is a non-static non-template member function of classX
with exactly one parameter of typeX
,X&
,const X&
,volatile X&
, orconst volatile X&
.
第19段定义了什么才算是用户声明的移动分配运算符:
Paragraph 19 defines what counts as user-declared move assignment operators:
用户声明的移动分配运算符
X::operator=
是非静态的 类X
的非模板成员函数,具有仅一个参数 键入X&&
,const X&&
,volatile X&&
或const volatile X&&
.
A user-declared move assignment operator
X::operator=
is a non-static non-template member function of classX
with exactly one parameter of typeX&&
,const X&&
,volatile X&&
, orconst volatile X&&
.
因此,它算作副本分配算子,而不算作移动分配算子.
So, it counts as a copy assignment operator, but not as a move assignment operator.
第18段告诉编译器何时生成副本分配运算符:
Paragraph 18 tells when the compiler generates copy assignment operators:
如果类定义未明确声明副本分配 运算符,一个隐式声明.如果类定义声明 移动构造函数或移动赋值运算符,隐式地 声明的副本分配运算符定义为已删除;否则, 被定义为默认值(8.4).如果以下情况不赞成使用后一种情况 类具有用户声明的副本构造函数或用户声明的 破坏者.
If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy assignment operator is defined as deleted; otherwise, it is defined as defaulted (8.4). The latter case is deprecated if the class has a user-declared copy constructor or a user-declared destructor.
第20段告诉我们编译器何时生成移动分配运算符:
Paragraph 20 tells us when the compiler generates move assignment operators:
如果类X的定义未明确声明移动 赋值运算符,如果满足以下条件,则将隐式声明为默认值: 并且仅当
[...]
— X没有用户声明的副本分配运算符,
[...]
If the definition of a class X does not explicitly declare a move assignment operator, one will be implicitly declared as defaulted if and only if
[...]
— X does not have a user-declared copy assignment operator,
[...]
由于该类具有用户声明的副本赋值运算符,因此编译器将不会生成任何隐式的赋值运算符.
Since the class has a user-declared copy assignment operator, neither of the implicit ones will be generated by the compiler.
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
时:
std::is_copy_assignable
and std::is_move_assignable
are described in table 49 as having the same value as, respectively is_assignable<T&,T const&>::value
and is_assignable<T&,T&&>::value
. That table tells us that is_assignable<T,U>::value
is true
when:
处理后的表达式
declval<T>() = declval<U>()
格式正确 作为未评估的操作数(第5条).访问检查的执行方式为 如果上下文与T
和U
无关.只有有效性 考虑分配表达式的直接上下文.
The expression
declval<T>() = declval<U>()
is well-formed when treated as an unevaluated operand (Clause 5). Access checking is performed as if in a context unrelated toT
andU
. Only the validity of the immediate context of the assignment expression is considered.
由于declval<T&>() = declval<T const&>()
和declval<T&>() = declval<T&&>()
的格式均适用于该类,因此它仍被视为可分配副本和可移动分配.
Since both declval<T&>() = declval<T const&>()
and declval<T&>() = declval<T&&>()
are well-formed for that class, it still counts as copy assignable and move assignable.
令人好奇的是,在存在move构造函数的情况下,operator=
可以正确执行移动,但从技术上讲,它不算作移动赋值运算符.如果该类没有复制构造函数,那就更奇怪了:它将具有一个复制赋值运算符,该运算符不执行复制操作,而仅执行移动操作.
As I mentioned in the comments, what's curious about all this is that, in the presence of a move constructor, that operator=
will correctly perform moves, but technically not count as a move assignment operator. It's even stranger if the class has no copy constructor: it will have a copy assignment operator that doesn't do copies, but only moves.
这篇关于用值而不是引用对赋值运算符进行分枝的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!