move constructor y copy constructor en C ++

Mi comprensión es que se llama a un constructor de movimiento si existe cuando devolvemos un objeto local de una función. Sin embargo, me encontré con una situación en la que se llamó al constructor de copias, como se muestra en el siguiente ejemplo en la funciónfoo2(). ¿Por qué sucedió eso?

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

El código anterior se compila cong++ --std=c++17 -fno-elide-constructors y la salida es:

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

Desde la salida, sabemos que se llama a un constructor de copias cuandofoo2() devoluciones*up para inicializar un @ temportNode objeto; ¿por qué no se llamó al constructor de movimiento en su lugar?

Respuestas a la pregunta(2)

Su respuesta a la pregunta