Move constructors
移动构造器
类的移动构造函数T
是一个非模板构造函数,其第一个参数是T&&
,,,const T&&
,,,volatile T&&
,或const volatile T&&
,或者没有其他参数,或者其余的参数都有默认值。
句法
class_name ( class_name && ) | (1) | (since C++11) |
---|---|---|
class_name ( class_name && ) = default; | (2) | (since C++11) |
class_name ( class_name && ) = delete; | (3) | (since C++11) |
解释
- 移动构造函数的典型声明。
- 强制编译器生成移动构造函数。
- 避免隐式移动构造函数。
选择的任何时候都会调用移动构造函数。过载分辨率,这通常发生在初始化28%直接初始化或复制初始化29%r值%28xvalue或prvalue%29%28,直到C++17%29xvalue%28,因为C++17%29具有相同类型,包括。
- 初始化:
T a = std::move(b或T
a(std::move(b),其中b
是类型的T;
- 函数参数传递:
f(std::move(a),在
哪里a是类
型的T和f
是v
oid f(T t);
- 函数返回:
return a;
在诸如T f()
,在哪里a
是类型的T
它有一个移动构造函数。
移动构造函数通常会“窃取”参数%28所持有的资源。指向动态分配的对象、文件描述符、TCP套接字、I/O流、正在运行的线程等的指针,而不是复制它们,并将参数保留在某种有效但不确定的状态。例如,从std::string
或者从一个std::vector
可能会导致该论点被保留为空。但是,不应依赖这种行为。对于某些类型,例如std::unique_ptr
,移出状态被完全指定。
隐式声明的移动构造函数
如果没有为类类型%28提供用户定义的移动构造函数struct
,,,class
,或union
%29,以下所有情况都是正确的:
- 没有用户声明的副本构造函数;
- 没有用户声明的副本分配操作符;
- 没有用户声明的移动分配操作符;
- 没有用户声明的析构函数;
the implicitly-declared move constructor is not defined as deleted due to conditions detailed in the next section, | (until C++14) |
---|
- 隐式声明的移动构造函数未定义为
删除
由于下一节的详细情况,%28直到C++14%29然后,编译器将将移动构造函数声明为非-显式inline public
具有签名的类的成员。T::T(T&&)
...一个类可以有多个移动构造函数,例如两个T::T(const T&&)
和T::T(T&&)
如果存在某些用户定义的移动构造函数,则用户仍然可能强制生成带有关键字的隐式声明的move构造函数。default
...隐式声明的%28或默认的第一次声明%29移动构造函数具有异常规范,如动态异常规范%28直到C++17%29异常规格%28自C++17%29。删除
隐式声明的移动构造函数类的隐式声明或默认移动构造函数。T
定义为删除
下列任何一项都是正确的:
T
不能移动%28的非静态数据成员是否已删除、不可访问或模棱两可的移动构造函数%29;
T
具有无法移动的直接或虚拟基类%28已删除、不可访问或模糊移动构造函数%29;
T
具有直接或虚拟基类的已删除或不可访问的析构函数;
T
是一个联合并且有一个具有非平凡复制构造函数的变体成员;
T has a non-static data member or a direct or virtual base without a move constructor that is not trivially copyable. | (until C++14) |
---|
T
具有非静态数据成员或直接或虚拟基,而没有移动构造函数,这些构造函数都是不可复制的。%28直到C++14%29已删除的隐式声明的移动构造函数被重载解析%28忽略,否则将阻止rvalue%29中的复制初始化。%28自C++14%29平凡移动构造函数类的移动构造函数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
类型时,隐式定义的移动构造函数通过以下方式复制对象表示%28std::memmove
29%。对于非工会类类型%28class
和struct
%29,移动构造函数按照初始化顺序对对象%27s基和非静态成员执行完全成员级移动,使用x值争论。
注记
为了使强异常保证成为可能,用户定义的移动构造函数不应抛出异常。例如,std::vector
倚靠std::move_if_noexcept
在需要重新定位元素时在移动和复制之间进行选择。
如果同时提供了复制和移动构造函数,则如果参数为r值
%28或aprvalue
例如无名的临时的或x值
如…的结果std::move
%29,如果参数是洛值
%28命名对象或返回lvalue引用%29的函数/运算符。如果只提供了复制构造函数,所有参数类别都会选择它%28,只要它引用Const,因为rvalue可以绑定到Const引用%29,这使得在移动不可用时复制移动回退。
在许多情况下,即使移动构造函数会产生明显的副作用,也可以优化它们,请参见复制省略...
当构造函数以rvalue引用作为参数时,它被称为%27移动构造函数%27。它没有义务移动任何东西,类不需要有要移动的资源,%27 Move构造函数%(5月27日)不能像允许的%28那样移动资源,但是在参数为Const值引用%28 const T&&%29的情况下,可能不合理。
例
二次
#include <string>
#include <iostream>
#include <iomanip>
#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) noexcept : s(std::move(o.s)) { }
};
A f(A a)
{
return a;
}
struct B : A
{
std::string s2;
int n;
// implicit move constructor B::(B&&)
// calls A's move constructor
// calls s2's move constructor
// and makes a bitwise copy of n
};
struct C : B
{
~C() { } // destructor prevents implicit move ctor C::(C&&)
};
struct D : B
{
D() { }
~D() { } // destructor would prevent implicit move ctor D::(D&&)
D(D&&) = default; // force a move ctor anyway
};
int main()
{
std::cout << "Trying to move A\n";
A a1 = f(A() // move-construct from rvalue temporary
A a2 = std::move(a1 // move-construct from xvalue
std::cout << "Trying to move B\n";
B b1;
std::cout << "Before move, b1.s = " << std::quoted(b1.s) << "\n";
B b2 = std::move(b1 // calls implicit move ctor
std::cout << "After move, b1.s = " << std::quoted(b1.s) << "\n";
std::cout << "Trying to move C\n";
C c1;
C c2 = std::move(c1 // calls the copy constructor
std::cout << "Trying to move D\n";
D d1;
D d2 = std::move(d1
}
二次
产出:
二次
Trying to move A
Trying to move B
Before move, b1.s = "test"
After move, b1.s = ""
Trying to move C
move failed!
Trying to move D
二次
© cppreference.com
在CreativeCommonsAttribution下授权-ShareAlike未移植许可v3.0。