使用move语义优化二进制算术运算
optimizing binary arithmetic operations using move semantics
我正在用一个简单的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调用运算符+()。我认为这个执行顺序是在语言规范中预定义的。编译器不应该首先执行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()
希望这能有所帮助。
相关文章:
- 算术运算的结果类似于:C浮点变量中的1/3
- 更快的C++算术运算
- 为什么循环体中的一个基本算术运算执行得比两个算术运算慢
- 为什么对无符号字符进行算术运算会将它们提升为有符号整数
- 如何在 C++ 中计算字符串的算术运算
- 复数的算术运算
- 具有非常非常大数字的算术运算
- 如何使用C++中的处理器指令来实现快速算术运算
- boost::spirit::x3 phrase_parse 在进入 Vector 之前进行算术运算
- C++中混合数据类型的算术运算
- C++:编译器是否优化整数 + 浮点算术运算?
- 如何在特征中对二维数组的列执行简单的算术运算
- 使用数字列表和算术运算获取目标数字
- C++:“enable_if”用于限制支持特定算术运算的类型
- 如何正确避免 SIGFPE 和算术运算溢出
- 编译器用于编译 128 位整数的基本算术运算的技巧
- 将临时值存储为某种数据类型时,算术运算的标准规则是什么
- 相同的算术运算在 C++ 和 Python 中给出不同的结果
- 使用move语义优化二进制算术运算
- 如何用非标准二进制表示法对数字进行算术运算