Move assignment operator
移动赋值算子
类的移动赋值算子T
是一个具有名称的非模板非静态成员函数。operator=
它只需要一个类型的参数。T&&
,,,const T&&
,,,volatile T&&
,或const volatile T&&
...
句法
class_name & class_name :: operator= ( class_name && ) | (1) | (since C++11) |
---|---|---|
class_name & class_name :: operator= ( class_name && ) = default; | (2) | (since C++11) |
class_name & class_name :: operator= ( class_name && ) = delete; | (3) | (since C++11) |
解释
- 移动赋值操作符的典型声明。
- 强制编译器生成移动赋值运算符。
- 避免隐式移动分配。
选择移动赋值运算符时,将调用它。过载分辨率例如,当一个对象出现在赋值表达式的左侧时,其中右边是相同或隐式可转换类型的rvalue。
移动赋值运算符通常“窃取”参数%28(例如)所持有的资源。指向动态分配的对象、文件描述符、TCP套接字、I/O流、正在运行的线程等的指针%29,而不是复制它们,并将参数保留在某种有效但不确定的状态。例如,从std::string
或者从一个std::vector
可能会导致该论点被保留为空。然而,这并不是一种保证。移动转让的定义要小于普通转让,而不是比普通转让定义更严格;如果普通转让必须在完成时留下两份数据,则只需要留下一份。
隐式声明移动赋值运算符
如果没有为类类型%28提供用户定义的移动赋值操作符struct
,,,class
,或union
%29,以下所有情况都是正确的:
- 没有用户声明的副本构造函数;
- 没有用户声明的移动构造函数;
- 没有用户声明的副本分配操作符;
- 没有用户声明的析构函数;
the implicitly-declared move assignment operator would not be defined as deleted, | (until C++14) |
---|
- 隐式声明的移动赋值运算符不会被定义为已
删除
,%28直到C++14%29然后,编译器将将移动赋值操作符声明为inline public
具有签名的类的成员。T& T::operator=(T&&)
...一个类可以有多个移动赋值操作符,例如T& T::operator=(const T&&)
和T& T::operator=(T&&)
如果存在某些用户定义的移动赋值运算符,则用户仍可能强制生成隐式声明的带有关键字的移动赋值运算符。default
...隐式声明的%28或默认的第一次声明%29移动赋值操作符有一个异常规范,如动态异常规范%28直到C++17%29异常规格%28自C++17%29。因为某些赋值运算符%28 Move或Copy%29总是为任何类声明,所以基类赋值运算符总是隐藏的。如果使用-声明用于从基类中引入赋值运算符,且其参数类型可能与派生类的隐式赋值运算符的参数类型相同,则使用-声明也被隐式声明隐藏。删除
隐式声明的移动赋值运算符类的隐式声明或默认移动赋值运算符。T
定义为删除
如果下列任何一项都是正确的:
T
具有非静态数据成员,const
;
T
具有引用类型的非静态数据成员;
T
有一个不能移动的非静态数据成员%28已删除、不可访问或模糊的移动赋值运算符%29;
T
具有不能移动的直接或虚拟基类--已删除、不可访问或模糊的移动分配运算符%29;
T has a non-static data member or a direct or virtual base without a move assignment operator that is not trivially copyable; T has a direct or indirect virtual base class. | (until C++14) |
---|
T
具有非静态数据成员或直接或虚拟基,而没有移动赋值操作符,该运算符不具有可复制性;
T
具有直接或间接的虚拟基类。
%28直到C++14%29
A deleted implicitly-declared move assignment operator is ignored by overload resolution. | (since C++14) |
---|
平凡移动赋值算子
类的移动赋值运算符T
如果以下所有内容都为真,则是微不足道的:
- 它不是用户提供的%28,它是隐式定义的或默认的%29;
T
没有虚拟成员功能;
T
没有虚拟基类;
- 的每个直接基选择的移动赋值运算符。
T
是微不足道的;
- 为每个非静态类类型%28或类类型%29成员的数组选择移动赋值运算符。
T
是微不足道的;
T has no non-static data members of volatile-qualified type. | (since C++14) |
---|
T
的非静态数据成员。易挥发-合格类型。
%28自C++14%29
平凡的移动赋值操作符执行与普通副本赋值操作符相同的操作,即使对象表示的副本看起来std::memmove
.所有与C语言%28POD类型%29兼容的数据类型都是可移动的。
隐式定义的移动赋值算子
如果隐式声明的移动赋值运算符既不删除也不平凡,则定义为%28,即编译器生成和编译函数体%29ODR-使用...
为union
类型时,隐式定义的移动赋值运算符将对象表示形式%28由std::memmove
29%。
对于非工会类类型%28class
和struct
%29,移动赋值操作符按照其声明顺序对对象%27s直接基和直接非静态成员执行完全成员级移动分配,使用标量内建分配、数组成员移动分配和称为非实际%29的类类型的移动赋值运算符。
As with copy assignment, it is unspecified whether virtual base class subobjects that are accessible through more than one path in the inheritance lattice, are assigned more than once by the implicitly-defined move assignment operator: struct V { V& operator=(V&& other) { // this may be called once or twice // if called twice, 'other' is the just-moved-from V subobject return *this; } }; struct A : virtual V { }; // operator= calls V::operator= struct B : virtual V { }; // operator= calls V::operator= struct C : B, A { }; // operator= calls B::operator=, then A::operator= // but they may only called V::operator= once int main() { C c1, c2; c2 = std::move(c1 } | (since C++14) |
---|
注记
如果同时提供了复制和移动赋值运算符,则如果参数为r值
%28或aprvalue
例如无名的临时的或x值
如…的结果std::move
%29,如果参数是洛值
%28命名对象或返回lvalue引用%29的函数/运算符。如果只提供副本分配,则所有参数类别都选择它%28,只要它按值或作为对Const的引用,因为rvalue可以绑定到Const引用%29,这使得在移动不可用时,复制分配成为移动分配的后盾。
未指定可通过继承格中的多条路径访问的虚拟基类次对象是否由隐式定义的移动赋值运算符%28同样分配给多个复制分配29%。
见赋值操作符过载有关用户定义的移动赋值操作符的预期行为的其他详细信息。
例
二次
#include <string>
#include <iostream>
#include <utility>
struct A
{
std::string s;
A() : s("test") { }
A(const A& o) : s(o.s) { std::cout << "move failed!\n"; }
A(A&& o) : s(std::move(o.s)) { }
A& operator=(const A& other)
{
s = other.s;
std::cout << "copy assigned\n";
return *this;
}
A& operator=(A&& other)
{
s = std::move(other.s
std::cout << "move assigned\n";
return *this;
}
};
A f(A a) { return a; }
struct B : A
{
std::string s2;
int n;
// implicit move assignment operator B& B::operator=(B&&)
// calls A's move assignment operator
// calls s2's move assignment operator
// and makes a bitwise copy of n
};
struct C : B
{
~C() { } // destructor prevents implicit move assignment
};
struct D : B
{
D() { }
~D() { } // destructor would prevent implicit move assignment
D& operator=(D&&) = default; // force a move assignment anyway
};
int main()
{
A a1, a2;
std::cout << "Trying to move-assign A from rvalue temporary\n";
a1 = f(A() // move-assignment from rvalue temporary
std::cout << "Trying to move-assign A from xvalue\n";
a2 = std::move(a1 // move-assignment from xvalue
std::cout << "Trying to move-assign B\n";
B b1, b2;
std::cout << "Before move, b1.s = \"" << b1.s << "\"\n";
b2 = std::move(b1 // calls implicit move assignment
std::cout << "After move, b1.s = \"" << b1.s << "\"\n";
std::cout << "Trying to move-assign C\n";
C c1, c2;
c2 = std::move(c1 // calls the copy assignment operator
std::cout << "Trying to move-assign D\n";
D d1, d2;
d2 = std::move(d1
}
二次
产出:
二次
Trying to move-assign A from rvalue temporary
move assigned
Trying to move-assign A from xvalue
move assigned
Trying to move-assign B
Before move, b1.s = "test"
move assigned
After move, b1.s = ""
Trying to move-assign C
copy assigned
Trying to move-assign D
move assigned
二次
© cppreference.com
在CreativeCommonsAttribution下授权-ShareAlike未移植许可v3.0。