【问题标题】:Copy elision and move semantics not working as expected复制省略和移动语义未按预期工作
【发布时间】:2015-07-20 13:34:08
【问题描述】:

我编写了一个程序来评估这样做之间的性能差异:

func3(func2(func1()));

对比这个:

retval1 = func1();
retval2 = func2(retval1);
func3(retval2);

我更喜欢后者,因为它的可读性和调试方便,我想知道编译器 (MSVC 12.0) 是否会优化发布版本中的中间对象。我的测试程序是这样的:

#include <iostream>

using namespace std;

struct Indicator {
    Indicator() {cout << "Default constructor" << endl;}
    Indicator(const Indicator& other) {cout << "Copy constructor" << endl;}
    const Indicator& operator=(const Indicator& other) {cout << "Assignment operator" << endl;}
    ~Indicator() {cout << "Destructor" << endl;}
};

Indicator func1()
{return Indicator();}

Indicator func2(Indicator&& i)
{return std::move(i);}

Indicator func3(Indicator&& i)
{return std::move(i);}

int main() {
    Indicator i = func3(func2(func1()));
    cout << &i << endl;
    return 0;
}

我惊讶地发现,即使使用 -O2,仍然会创建三个 Indicator 实例:

Default constructor
Copy constructor
Copy constructor
Destructor
Destructor
00000000002EFC70
Destructor
Press <RETURN> to close this window...

这与我对移动语义的理解相冲突,即在这种情况下应该只创建一个Indicator 的实例。我还认为编译器应该能够将 NRVO 用于链式函数调用。有人可以向我解释这里发生了什么吗?

【问题讨论】:

    标签: c++ optimization


    【解决方案1】:

    根据您定义复制构造函数和复制赋值运算符时的rule of 5,您禁用了编译器生成的移动构造函数和移动赋值运算符。

    如果你定义你的移动构造函数,你会得到你期望的输出。

    Indicator(Indicator&& other) {cout << "Move constructor" << endl;}
    Indicator& operator=(Indicator&& other) {cout << "Move assignment operator" << endl;}
    

    Working demo

    【讨论】:

    • 值得注意的是,Visual C++ 12 根本不生成隐式移动构造函数。
    • 啊。好的我知道了。我刚刚习惯了遵守 3 的规则,现在我必须记住定义移动构造函数......
    • @Carlton 或者坚持零规则!如果您不必必须定义其中的一个(或部分),那么让编译器为您制作它们!
    猜你喜欢
    • 2021-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-08
    • 2015-03-09
    • 2018-12-15
    相关资源
    最近更新 更多