【问题标题】:Why does not apply RVO in this code为什么不在此代码中应用 RVO
【发布时间】:2016-07-14 14:11:39
【问题描述】:

有一个带有构造函数的 Complex 类,它为 RVO 打印一条消息。
我已经在 gtest 中测试了 Complex 的 operator+ 方法。
如果发生 RVO,则打印“Complex!!”留言 3 次。
但是有“复杂!!”消息 5 次。
我认为没有发生 RVO。
我用 c++98 和 c++11 编译了这段代码
为什么不发生RVO?

#include <stdio.h>

class Complex {
    friend Complex operator+(const Complex&, const Complex&);
public:
    Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) { printf("\nComplex!!\n");}

    Complex(const Complex& c) : real(c.real), imag(c.imag) {}

    Complex& operator=(const Complex& c) {
        real = c.real;
        imag = c.imag;

        return *this;
    }

    ~Complex() {}
private:
    double real;
    double imag;
};

Complex operator+(const Complex& lhs, const Complex& rhs)
{
    return Complex(lhs.real + rhs.real, lhs.imag + rhs.imag);
}

int main()
{
    Complex a(1.0), b(2.0), c;

    for (int i = 0; i < 2; i++) {
        c = a + b;
    }
}

【问题讨论】:

  • 有 5 个构造函数调用:一个用于初始化 abc,每个调用一个用于 operator+。为什么你认为会有其他数字?你认为哪些不应该发生?
  • RVO 省略了对复制构造函数和移动构造函数的调用,而不是其他调用。所以你的代码没有告诉你任何关于 RVO 的信息。
  • Complex c = a + b; 可能会发生这种情况。现在你只有一个作业,这是不适用的。
  • 发生RVO时,两个“Complex!!”不应打印调用 operator+ 的消息。但它是打印出来的。
  • @GyeongWonDo:那是错误的。您误解了省略的作用。

标签: c++ rvo


【解决方案1】:

RVO 不是防止对象被构造的优化 - 它是避免不必要的额外复制或移动的优化。

您的示例是构造三个对象(abc),然后再构造两个对象(a+b 在循环中两次)。这些对象都必须被构造,没有办法围绕它进行优化 - 编译器无法分解operator+中的Complex()临时初始化并将其解压缩到real的分配中imagoperator= 内。

如果您检测了复制和移动构造函数,您会发现在您的示例中没有调用它们。但他们本来可以的。在operator+() 中创建的临时对象在概念上移动到函数的返回中,然后绑定到Complex::operator=() 中的引用。 那个动作被 RVO 省略了,如果你用-fno-elide-constructors 编译,你会看到这个动作。

【讨论】:

  • 我想删除在 operator+() 中创建的临时文件。在这种情况下,如何使用 RVO 删除这个临时文件?
  • @GyeongWonDo 这不是你必须的事情。如果你写了Complex d = a + b;,那将不会执行复制/移动——它会在d 中就地构建临时的。但是operator+()必须构造一个对象。
  • 我已经检查了复制构造函数是使用 -fno-elide-constructors 选项调用的。但是如果没有 -fno-elide-constructors 选项,则不会调用复制构造函数。你是说这是RVO?
  • 我还有一个问题。我在哪里可以阅读有关 RVO 的信息?具体什么时候可以发生RVO?
  • @GyeongWonDo 是的,那个复制/移动被省略了。
【解决方案2】:

返回值优化是复制省略的一种形式。简单来说,它是一种避免复制对象的优化。它不会避免通过其他方式创建对象。

可以通过观察复制和移动构造函数的副作用来验证是否应用了RVO。

您的复制构造函数没有副作用,因此无法观察 RVO 是否已应用。

当 RVO 发生时,两个“复杂!!”不应打印调用 operator+ 的消息。

没有。这些消息打印在类的常规(非复制)构造函数中。 RVO 对调用常规构造函数的次数没有影响。

【讨论】:

  • 谢谢,我在复制构造函数中写了打印一些消息(副作用)。我使用 -fno-elide-constructors 选项检查了是否调用了复制构造函数
猜你喜欢
  • 2018-08-29
  • 2013-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多