在C ++中移动构造函数和复制构造函数

时间:2018-05-16 16:04:46

标签: c++ copy-constructor move-constructor

我的理解是当从函数返回本地对象时,如果它存在,则调用移动构造函数。但是,我遇到了调用复制构造函数的情况,如函数foo2()中的以下示例所示。为什么会这样?

#include <cstdio>
#include <memory>
#include <thread>
#include <chrono>

class tNode
{
public:
    tNode(int b = 10)
    {
        a = b;
        printf("a: %d, default constructor %s() is called at %s:%d \n", a, __func__, __FILE__, __LINE__);
    }

    tNode(const tNode& node)
    {
        a = node.a;
        printf("a: %d, copy constructor %s() is called at %s:%d \n", a, __func__, __FILE__, __LINE__);
    }

    tNode& operator=(const tNode& node)
    {
        a = node.a;
        printf("a: %d, copy assignment %s() is called at %s:%d \n", a, __func__, __FILE__, __LINE__);
    }

    tNode(tNode&& node)
    {
        a = node.a;
        printf("a: %d, move constructor %s() is called at %s:%d \n", a, __func__, __FILE__, __LINE__);
    }

    tNode& operator=(tNode&& node)
    {
        a = node.a;
        printf("a: %d, move assignment %s() is called at %s:%d \n", a, __func__, __FILE__, __LINE__);
    }

    ~tNode() { printf("a: %d, destructor %s() is called at %s:%d \n", a, __func__, __FILE__, __LINE__); }

private:
    int a = 0;
};

tNode foo()
{
    tNode node;
    return node;
}

tNode foo2()
{
    std::unique_ptr<tNode> up = std::make_unique<tNode>(20);
    return *up;
}

int main()
{
    {
        tNode n1 = foo();
        tNode n2 = foo2();
    }

    // we pause here to watch how objects are created, copied/moved, and destroyed.
    while (true)
    {
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }

    return 0;
}

以上代码是使用g++ --std=c++17 -fno-elide-constructors编译的 输出是:

a: 10, default constructor tNode() is called at testCopyControl.cpp:13
a: 10, move constructor tNode() is called at testCopyControl.cpp:31
a: 10, destructor ~tNode() is called at testCopyControl.cpp:40
a: 10, move constructor tNode() is called at testCopyControl.cpp:31
a: 10, destructor ~tNode() is called at testCopyControl.cpp:40
a: 20, default constructor tNode() is called at testCopyControl.cpp:13
a: 20, copy constructor tNode() is called at testCopyControl.cpp:19
a: 20, destructor ~tNode() is called at testCopyControl.cpp:40
a: 20, move constructor tNode() is called at testCopyControl.cpp:31
a: 20, destructor ~tNode() is called at testCopyControl.cpp:40
a: 20, destructor ~tNode() is called at testCopyControl.cpp:40
a: 10, destructor ~tNode() is called at testCopyControl.cpp:40

从输出中,我们知道当foo2()返回*up以初始化临时tNode对象时,将调用复制构造函数;为什么不调用移动构造函数呢?

2 个答案:

答案 0 :(得分:4)

tNode foo()
{
    tNode node;
    return node;
}

tNode n1 = foo();

负责

的输出
a: 10,  tNode() is called at testCopyControl.cpp:13
a: 10,  move constructor tNode() is called at testCopyControl.cpp:31
a: 10, destructor ~tNode() is called at testCopyControl.cpp:40
a: 10,  move constructor tNode() is called at testCopyControl.cpp:31
a: 10, destructor ~tNode() is called at testCopyControl.cpp:40

你看到的是被调用的默认构造函数,然后node开始在return语句中被视为rvalue,将其移动到返回值,然后从返回值转换为n1

使用

tNode foo2()
{
    std::unique_ptr<tNode> up = std::make_unique<tNode>(20);
    return *up;
}

由于您没有返回函数本地对象,因此行为不同。 *up为您提供tNode&,因此return语句不能将其视为右值。由于它是左值,您必须调用复制构造函数将其复制到返回值中。然后,与第一个示例一样,调用移动构造函数将对象从返回值移动到n2

答案 1 :(得分:4)

以下代码不会隐式移动构造的对象:

tNode foo2()
{
    std::unique_ptr<tNode> up = std::make_unique<tNode>(20);
    return *up;
}

这是因为,无论我们看起来多么明显/直观,编译器都无法证明从up包含的对象移动是安全的。它被迫通过复制返回。

你可以强制它通过显式地转换对象来返回R值:

tNode foo2()
{
    std::unique_ptr<tNode> up = std::make_unique<tNode>(20);
    return std::move(*up);
}