使用移动语义优化二进制算术运算

时间:2013-12-15 07:40:01

标签: c++ optimization c++11 move-semantics rvalue-reference

我正在尝试使用简单的rvalue类进行Vector引用,试图消除二进制操作中不需要的临时值。经过一番努力,我发现operator+()有以下两个重载:

// overload called if right = lvalue and left = lvalue/rvalue
friend Vector<T> operator+(Vector<T> a, const Vector<T>& b) {
  a += b;
  return a;
}

// overload called if right = rvalue and left = lvalue/rvalue
friend Vector<T> operator+(const Vector<T>& a, Vector<T>&& b) {
  b += a;
  return std::move(b);
}

我可以确保在auto x = a+b+c+d+...;之类的表达式中,只要至少ab是临时的,就会调用移动构造函数而不创建任何新的临时值。

另一方面,即使ab之后的一个值(比如说d)是lvalue,这在技术上应该足以避免复制。编译器可以通过扫描给定的表达式找到至少一个临时表并根据该值开始调用operator+来进行任何优化吗?

示例1:

#include "vector.h"

Vector<double> get() {
  return {0, 1, 2, 6};
}
int main (){
  auto a = get();
  auto b = get();
  auto c = a + get() + b;
  std::cout << c << std::endl;

  return 0;
}

输出:

calling move ctor
calling move ctor
[0, 3, 6, 18]

示例2:

#include "vector.h"

Vector<double> get() {
  return {0, 1, 2, 6};
}
int main (){
  auto a = get();
  auto b = get();
  auto c = a + b + get();
  std::cout << c << std::endl;

  return 0;
}

输出:

calling copy ctor
calling move ctor
[0, 3, 6, 18]

1 个答案:

答案 0 :(得分:2)

我猜答案是否定的。在你的代码中

auto c = a + b + get();

编译器必须首先在a和b上调用operator +()。我认为这个执行顺序是在laguage规范中预定义的。编译器不应该首先执行b + get(),因为不同的执行顺序可能导致(a + b + get())的不同返回值,以及不同的副作用。所以执行的顺序应该保持不变。

首先,编译器可以用+ b部分做什么?由于a和b都是l值,编译器必须选择一个带有两个l值的参数的函数。并不是编译器开发人员不能添加新的优化,而是编译器应该只按语言中的指定进行。假设编译器将函数的r值版本用于a + b。然后,在你的函数中,a或b将被修改(因为它们是r值),这是不可取的:为什么我们需要修改操作数来获得它们的总和?

如果您愿意在函数中进行a和b修改,可以在调用函数之前将它们转换为r值,例如std::move(a) + std::move(b) + get()

希望这有帮助。