我已经阅读了几篇文章,说在返回局部变量时允许移动语义的函数签名应该看起来与它按值返回完全一样,只要定义了移动构造函数/移动赋值运算符,就会发生RVO或移动语义视情况而定 .

虽然我读过许多人说你的返回类型几乎从不是一个右值引用,因为它在本地移动变量的情况下返回一个悬空引用或者可以阻止RVO,我想知道当移动对象时它是否正确具有超出函数返回范围的生命周期,例如移动将继续存在的对象的全局对象或成员对象 .

这是一个小测试程序来说明我的问题:

#include <iostream>
using namespace std;

class Test
{
    public:
        Test() = delete;
        Test(int _x)
        {
            cout << "Test(int) x: " << _x << endl;

            if(nullptr != x)
                delete x;

            x = new int(_x);
        }

        ~Test()
        {
            cout << "~Test() x: ";

            if(nullptr != x)
            {
                cout << *x;
                delete x;
            }
            else
                cout << "nullptr";

            cout << endl;
        }

        Test(const Test &rhs)
        {
            cout << "Test(const Test&) rhs x: ";

            if(nullptr != rhs.x)
            {
                cout << *(rhs.x);

                if(nullptr != x)
                    delete x;

                x = new int(*(rhs.x));
            }
            else
            {
                cout << "nullptr";

                if(nullptr != x)
                    delete x;

                x = nullptr;
            }

            cout << endl;
        }

        Test(Test &&rhs)
        {
            cout << "Test(Test&&) rhs x: ";

            if(nullptr != rhs.x)
                cout << *(rhs.x);
            else
                cout << "nullptr";

            cout << endl;

            if(nullptr != x)
                delete x;

            x = rhs.x;
            rhs.x = nullptr;
        }

        Test& operator=(const Test &rhs)
        {
            cout << "operator=(const Test&) rhs x: ";

            if(nullptr != rhs.x)
            {
                cout << *(rhs.x);

                if(nullptr != x)
                    delete x;

                x = new int(*(rhs.x));
            }
            else
            {
                cout << "nullptr";

                if(nullptr != x)
                    delete x;

                x = nullptr;
            }

            cout << endl;
            return *this;
        }

        Test& operator=(Test &&rhs)
        {
            cout << "operator=(Test&&) rhs x: ";

            if(nullptr != rhs.x)
                cout << *(rhs.x);
            else
                cout << "nullptr";

            cout << endl;

            if(nullptr != x)
                delete x;

            x = rhs.x;
            rhs.x = nullptr;
            return *this;
        }

    private:
        int *x = nullptr;
};

Test global_test(7);

Test&& get_test()
{
    return std::move(global_test);
}

int main()
{
    Test local_test(5);
    local_test = get_test();
    //auto local_test = get_test();

    return 0;
}

使用get_test()显式返回rvalue引用,上面的输出是:

Test(int) x: 7
Test(int) x: 5
operator=(Test&&) rhs x: 7
~Test() x: 7
~Test() x: nullptr

这正是我想要的,只有在现有测试窃取另一个非本地测试数据时才调用移动赋值运算符 . 但是如果功能改为:

Test get_test()

然后输出变为:

Test(int) x: 7
Test(int) x: 5
Test(Test&&) rhs x: 7
operator=(Test&&) rhs x: 7
~Test() x: nullptr
~Test() x: 7
~Test() x: nullptr

这是好的,因为没有复制正在发生,但它仍然比原来慢,因为看起来编译器从移动的global_test创建临时Test对象,然后移动将其分配到local_test .

如果我更改主要注释的行:

int main()
{
    //Test local_test(5);
    //local_test = get_test();
    auto local_test = get_test();

    return 0;
}

并使get_test()显式返回原始示例中的右值引用,输出显示:

Test(int) x: 7
Test(Test&&) rhs x: 7
~Test() x: 7
~Test() x: nullptr

这是我们想要的,但再次将签名更改为:

Test get_test()

根本不会改变输出 . 可能是当使用get_test()构造local_test时,get_test()基本上是内联的 . 在这种情况下,当被移动的对象不是本地时,是否可以显式返回右值引用?显式返回右值引用的唯一时间可以在移动的对象是本地时阻止RVO吗?

EDIT: 使用默认优化级别编译:

g++ -Wall -std=c++11 test.cpp

尽管使用-O3进行编译并未改变任何结果 .