Sherlock的程序人生

C++编译器的RVO和NRVO

1、说明

我一直记得返回对象的函数在调用时会有拷贝构造动作,但是最近实际测试却和记忆有些偏差,经查询是编译的问题

RVO: return value optimization

NRVO: named return value optimization

这两个是编译器的一种函数返回值优化策略

先说结果,VS在debug模式下默认 RVO,release模式下默认 NRVO;而g++在debug和release下都默认NRVO

2、示例

先看一组代码

class Test
{
public:
    explicit Test(int num)
        : num(num)
    {
        cout << "constructor " << num << endl;
    }

    Test(const Test &test)
    {
        cout << "copy constructor " << test.num << endl;
        num = test.num;
    }

    ~Test()
    {
        cout << "destructor " << num << endl;
    }

    void print() const
    {
        cout << "print " << num << endl;
    }
private:
    int num{};
};

Test getTest(int num)
{
    Test test(num);
    return test;
}

int main()
{
    Test test2 = getTest(12);
    test2.print();
    return 0;
}

函数 getTest() 返回一个对象,main() 函数中调用并复制给变量理应有一个拷贝构造的动作,但是实际上返回值为

constructor 12
print 12
destructor 12

打印变量地址也发现,getTest() 函数内的变量 test 和 main() 函数中的变量 test 的地址居然是一样的。是我记错了吗?其实不是,根据C++语法,确实应该有拷贝构造的动作,这里的结果是编译器优化的后的,就是上文所说的 RVO 和 NRVO

3、编译器优化

g++ 可以使用参数 -fno-elide-constructors 来关闭优化,CMakeList 使用以下代码关闭

add_compile_options(-fno-elide-constructors)

或者

set(CMAKE_CXX_FLAGS “-fno-elide-constructors ${CMAKE_CXX_FLAGS}”)

如果我们关闭编译器的优化,最后输出的结果应该是

constructor 12		//getTest()函数内构造test对象
copy constructor 12	//getTest()的返回值不能是test对象,需要一个临时变量,使用test对象拷贝构造临时对象_test
destructor 12		//getTest()返回,test对象被析构
copy constructor 12	//main()函数使用test2变量接收临时对象_test
destructor 12		//临时对象_test被析构
print 12		//print()调用
destructor 12		//main()函数内test2对象被析构

不难理解,我的记忆没错,根据C++语法确实应该有拷贝构造,只不过是编译器优化了

posted @ 2021-10-20 13:14  sherlock_lin  阅读(473)  评论(0编辑  收藏  举报