代码之家  ›  专栏  ›  技术社区  ›  friartuck

调用std::在返回值上移动-签名应该是什么

  •  2
  • friartuck  · 技术社区  · 7 年前

    考虑

    class X
    {
    public:
        std::unique_ptr<int> m_sp;
        A m_a;
    
        A test1()
        {
            return std::move(m_a);
        }
    
        A&& test2()
        {
            return std::move(m_a);
        } 
    
        std::unique_ptr<int> test3()
        {
            return std::move(m_sp);
        }
    
        std::unique_ptr<int>&& test4()
        {
            return std::move(m_sp);
        }
    
        std::unique_ptr<int> test5()
        {
            return std::make_unique<int>(50);
        }
    };
    
    class A
    {
    public:
        A()
        {
            m_i = 1;
        }
    
        A(A&& other)
        {
            this->m_i = other.m_i;
            other.m_i = -1;
        }
    
        A& operator=(A&& other)
        {
            this->m_i = other.m_i;
            other.m_i = -1;
            return *this;
        }
    
        int m_i;
    };
    

    练习这些课程

    X x;
    A y;
    y.m_i = 10;
    y = x.test1();
    
    X x2;
    A y2;
    y2.m_i = 10;
    y2 = x2.test2();
    

    两者都调用A的移动赋值,但只有在test1情况下,我们才调用A的移动构造函数。为什么呢?是因为我们不能退回a&&(标准::移动将向A&&,但是test1说它必须返回一个A)。

    通常,当您想要移动/转移昂贵成员变量的所有权时,是否要将返回值指定为右值引用(A&&)还是左值(A)类型?

    感觉有点不自然,好像您没有使用成员变量,您让RVO/NRVO做它该做的事情,只返回一个左值。以unique_ptr为例,当您有一个自动变量时,您有一个类似test5()的签名,但是如果您有一个不适合RVO/NRVO的变量,如成员变量,则应首选test3或test4的签名。

    我很想知道。

    谢谢

    1 回复  |  直到 7 年前
        1
  •  6
  •   Michael Kenzel    7 年前

    这是语义上的区别。当您返回类似于中的对象时

    A test1()
    {
        return std::move(m_a);
    }
    
    std::unique_ptr<int> test3()
    {
        return std::move(m_sp);
    }
    

    然后你总是离开你的会员。无论调用者是否使用返回值执行某些操作,您都将离开您的应用程序 X 变成临时的。所有权不再属于你。调用方可以接管返回值。如果调用方忽略返回值,则临时值将被销毁。另一方面,如果返回一个右值引用,如

    A&& test2()
    {
        return std::move(m_a);
    }
    
    std::unique_ptr<int>&& test4()
    {
        return std::move(m_sp);
    }
    

    您只是为调用者提供了从对象的所有权转移/接管对象所有权的机会。如果调用者不执行移动,您的 X 将保留所有权,对象将不会移动。

    要理解的关键是,与名字所暗示的相反, std::move() 实际上不执行移动。它只允许移动给定对象。实际移动由相应类型的移动构造函数或移动赋值运算符执行。