move constructor and copy constructor in C++

荒凉一梦 提交于 2019-12-01 06:49:10

问题


My understanding is that a move constructor is called if it exists when we return a local object from a function. However, I ran into a situation where the copy constructor was called instead, as shown in the following example in function foo2(). Why did that happen?

#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;
}

The above code is compiled with g++ --std=c++17 -fno-elide-constructors and the output is:

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

From the output, we know that a copy constructor is called when foo2() returns *up to initialize a temporary tNode object; why did the move constructor not get called instead?


回答1:


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

and

tNode n1 = foo();

Is responsible for the output of

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

And what you see is the default constructor being called and then node begin treated as an rvalue in the return statement to move it into the return value and then another move from the return value into n1

With

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

The behavior is different as you are not returning a function local object. *up gives you a tNode& so the return statement can't treat it as an rvalue. Since it is an lvalue you have to call the copy constructor to copy it into the return value. Then, like the first example, the move constructor is called to move the object from the return value into n2.




回答2:


The following code does not implicitly move the constructed object:

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

This is because, however obvious/intuitive it might seem to us, the compiler cannot prove that it is safe to move-from the object contained by up. It's forced to return by copy.

You could force it to return by R-value by explicitly casting the object as such:

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


来源:https://stackoverflow.com/questions/50375451/move-constructor-and-copy-constructor-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!