c++ - 使用 move 语义优化二进制算术运算

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

我正在用一个简单的 Vector 类尝试使用 rvalue 引用,试图消除二进制运算中不需要的临时变量。经过一番努力,我发现对于 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+...; 这样的表达式中,只要至少有 a b 是一个临时变量,将调用 move 构造函数而不创建任何新的临时变量。

另一方面,即使 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]

最佳答案

我想答案是否定的。在您的代码中

auto c = a + b + get();

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

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

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

希望这有帮助。

关于c++ - 使用 move 语义优化二进制算术运算,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20592336/

相关文章:

c++ - 基于范围的 for 循环等价物

c++ - librdkafka c++ 库是否有模拟服务器或代理?

css - 如何优化 CSS background-image 和 background-position 属性

java - 更有效地存储(字符串,整数)元组并应用二分搜索

java - Java编译器优化

c++ - Initializer-list-构造一个不可复制(但可 move )对象的 vector

c++ - 为什么不能在没有可变参数的情况下在 lambda 内部转发参数?

c++ - C++中std::cin对象的规则是什么?

c++ - 多项目 cmake 不工作

c++ - 变量类派生自某个抽象类的类模板