Skip to the content.

cppreference对移动构造函数的解释

Contact me


类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)

解释

  1. 移动构造函数的声明
  2. 强制编译器产生移动构造函数
  3. 拒绝隐式的移动构造函数

当一个对象从相同类型的[右值(xvalueprvalue) until C++17xvalue since C++17]初始化(直接初始化或拷贝初始化)时,移动构造函数被调用,包括:

当初始化prvalue,移动构造函数[通常被优化until C++17,不被调用since C++17],参见拷贝省略

移动构造函数通常“窃取”参数的资源(例如动态分配对象的指针,文件,TCP,I/O流等),而不是进行拷贝,这导致参数处于可用或者某个中间状态。例如移动std::stringstd::vector会导致参数不存在,但是这个行为也不确定,比如std::unique_ptr移动的状态就是确定的。

移动构造函数的隐式声明

如果用户没有对类(struct, class, union)定义移动构造函数,并且以下所有条件为真:

那么编译器会给类声明一个隐式的inline public移动构造函数,签名为T::T(T&&)

一个类可以有多个移动构造函数,例如T::T(const T&&)T::T(T &&),如果用户定义了一部分,那么用户任然可以通过default关键字强制生成移动构造函数。

隐式声明的,或声明为default的移动构造函数的异常参考[dynamic exception specification until C++17 exception specification since C++17]

不产生隐式声明移动构造函数的情况

类T不产生隐式声明或默认移动构造函数,当以下部分条件为真:

不产生隐式声明移动构造函数会被重载决议忽略 since C++14

平凡的移动构造函数

以下条件全部为真时,类T的构造函数时平凡的:

平凡移动构造函数的行为和普通拷贝函数一样,使用std::memmove拷贝对象。所有C语言的类型可以平凡移动。

隐式定义的移动构造函数

如果隐式声明的移动构造函数没有被删除也不平凡,编译器会位置定义。对于union类型,隐式定义的移动构造函数通过std::memmove拷贝,对于非uunion的类型(classstruct)移动构造函数对所有成员和非静态成员移动。

例子

#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 constructor C::(C&&)
};

struct D : B
{
    D() { }
    ~D() { }          // destructor would prevent implicit move constructor D::(D&&)
    D(D&&) = default; // forces a move constructor anyway
};

int main()
{
    std::cout << "Trying to move A\n";
    A a1 = f(A()); // move-constructs from rvalue temporary
    A a2 = std::move(a1); // move-constructs 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 constructor
    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 copy constructor

    std::cout << "Trying to move D\n";
    D d1;
    D d2 = std::move(d1);
}

output:

    Trying to move A
    Trying to move B
    Before move, b1.s = "test"
    After move, b1.s = ""
    Trying to move C
    move failed!

不得不说真的很复杂。。。