переместить конструктор и скопировать конструктор в C ++ - PullRequest
0 голосов
/ 16 мая 2018

Насколько я понимаю, конструктор перемещения вызывается, если он существует, когда мы возвращаем локальный объект из функции. Однако я столкнулся с ситуацией, когда вместо этого вызывался конструктор копирования, как показано в следующем примере в функции 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 голосов
/ 16 мая 2018

Следующий код неявно перемещает построенный объект:

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);
}
0 голосов
/ 16 мая 2018
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 начинает обрабатываться как rvalue в операторе return, чтобы переместить его в возвращаемое значение, а затем еще одно перемещение из возвращаемого значения в n1

С

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

Поведение отличается тем, что вы не возвращаете локальный объект функции. *up дает вам tNode&, поэтому оператор return не может рассматривать его как значение. Поскольку это lvalue, вы должны вызвать конструктор копирования, чтобы скопировать его в возвращаемое значение. Затем, как и в первом примере, вызывается конструктор перемещения для перемещения объекта из возвращаемого значения в n2.

...