我正在用一个简单的 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 构造函数而不创建任何新的临时变量。
另一方面,即使 a
和 b
之后的值(比如 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/