了解函数返回的对象的移动语义

Understanding move semantics with objects returned by a function

我从 learncpp.com 中看到以下程序作为理解移动语义的示例。我尝试 运行 这个程序,看看程序流程是如何按照教程中的解释工作的。

但是,我在函数调用中没看懂。

Auto_ptr4<Resource> generateResource()
{
    Auto_ptr4<Resource> res(new Resource);
    return res; // this return value will invoke the move constructor
}

据说会使用移动构造函数,但是当我设置断点和调试时它根本没有这样做。

谁能帮我理解为什么这里不调用移动构造函数?编译器是否在这里省略?如果是这样,如何修改它以便调用移动构造函数。请解释这里发生了什么。

此外,如果您能分享一些调用移动构造函数的位置以及如何使用移动构造函数代替复制构造函数的示例,那就太好了。

#include <iostream>

template<class T>
class Auto_ptr4
{
    T* m_ptr;
public:
    Auto_ptr4(T* ptr = nullptr)
    :m_ptr(ptr)
    {
    }

    ~Auto_ptr4()
    {
        delete m_ptr;
    }

    // Copy constructor
    // Do deep copy of a.m_ptr to m_ptr
    Auto_ptr4(const Auto_ptr4& a)
    {
        m_ptr = new T;
        *m_ptr = *a.m_ptr;
    }

    // Move constructor
    // Transfer ownership of a.m_mptr to m_ptr
    Auto_ptr4(Auto_ptr4&& a)
    : m_ptr(a.m_ptr)
    {
        a.m_ptr = nullptr;
    }

    // Copy assignment
    // Do deep copy of a.m_ptr to m_ptr
    Auto_ptr4& operator=(const Auto_ptr4& a)
    {
        // Self-assignment detection
        if (&a == this)
            return *this;

        // Release any resource we're holding
        delete m_ptr;

        // Copy the resource
        m_ptr = new T;
        *m_ptr = *a.m_ptr;

        return *this;
    }

    // Move assignment
    // Transfer ownership of a.m_ptr to m_ptr
    Auto_ptr4& operator=(Auto_ptr4&& a)
    {
        // Self-assignment detection
        if (&a == this)
            return *this;

        // Release any resource we're holding
        delete m_ptr;

        // Transfer ownership of a.m_ptr to m_ptr
        m_ptr = a.m_ptr;
        a.m_ptr = nullptr;

        return *this;
    }

    T& operator*() const { return *m_ptr; }
    T* operator->() const { return m_ptr; }
    bool isNull() const { return m_ptr == nullptr; }
};

class Resource
{
public:
    Resource() { std::cout << "Resource acquired\n"; }
    ~Resource() { std::cout << "Resource destroyed\n"; }
};

Auto_ptr4<Resource> generateResource()
{
    Auto_ptr4<Resource> res(new Resource);
    return res; // this return value will invoke the move constructor
}

int main()
{
    Auto_ptr4<Resource> mainres;
    mainres = generateResource(); // this assignment will invoke the move assignment

    return 0;
}

谢谢

Auto_ptr4<Resource> generateResource()
{
    Auto_ptr4<Resource> res(new Resource);
    return res; // this return value will invoke the move constructor
}

您的代码是以一种非常允许复制省略的方式编写的。 C++ 标准有一整节专门讨论这个用例。

[class.copy.elision/1.1]

in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function parameter or a variable introduced by the exception-declaration of a handler ([except.handle])) with the same type (ignoring cv-qualification) as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function call's return object

如果你想防止省略,你需要return一个不能被合法省略的表达式。幸运的是,在这种情况下也很容易强制调用移动构造函数:

Auto_ptr4<Resource> generateResource()
{
    Auto_ptr4<Resource> res(new Resource);
    return std::move(res); // this return value will invoke the move constructor
}

自此表达式不再是自动变量名,Auto_ptr4<Resource>&& 也不完全等同于 Auto_ptr4<Resource>。 c'tor 没有被删除。

这对于学习目的来说很好,但不要在实际代码中这样做。复制省略是一件非常好的事情,可以提高代码的效率。让编译器在可能的时候为你做。