C++数组运算符开销
C++ array operator overhead
我记得以前读过一些代码,这些代码允许编译器做一些工作并简化像下面这样的表达式:
// edit: yes the parameters where meant to be passed by reference
// and maintain constness sorry !
template< typename T >
std::vector<T> operator+( const std::vector<T>& a, const std::vector<T>& b )
{
assert( a.size() == b.size() );
std::vector<T> o; o.reserve( a.size() );
for( std::vector<T>::size_type i = 0; i < a.size(); ++i )
o[i] = a[i] + b[i];
return o;
}
// same for operator* but a[i] * b[i] instead
std::vector<double> a, b, c, d, e;
// do some initialization of the vectors
e = a * b + c * d
在通常情况下,会为每个运算符创建并分配一个新的矢量,而编译器只会创建一个副本并对其执行所有操作
这是什么技术?
正如@Agnew很早就提到的,您所描述的技术是表达式模板
这通常是用向量1的数学概念来完成的,而不是std::vector
。大致的笔画是:
-
不需要对向量进行数学运算即可返回结果。相反,让它们返回一个代表最终需要执行的操作的代理对象。
a * b
可以返回一个"乘法代理"对象,该对象只包含对应该相乘的两个向量的常量引用。 -
也为这些代理编写数学运算,允许它们链接在一起,因此
a * b + c * d
变成(TempMulProxy) + (TempMulProxy)
变成(TempAddProxy)
,所有这些都不需要进行任何数学运算或复制任何向量。 -
编写一个赋值运算符,将代理对象作为右侧对象。这个操作符可以看到整个表达式
a * b + c * d
,并在知道目的地的情况下对向量有效地执行该操作。全部不创建多个临时矢量对象。
1或矩阵或四元数等…*
这里没有问题。然而,我的水晶球告诉我,你想知道你想出的两种方法中更好的方法,以便对a * b + c * d
这样的向量执行分量算术运算,其中a
、b
、c
、d
是大小相同的向量(std::vector<T>
):
-
对于要执行的每个操作,在元素上循环,执行计算并返回结果向量。把这些运算放在一个向量公式中。
-
对于输入向量中的每个元素,计算整个表达式,并将其写入一个最终结果向量中。
有两件事需要考虑:
- 性能:这里,第二个选项在前面,因为处理器不会分配不必要的临时向量
- 可重用性:很明显,实现向量的算法操作并通过简单地在向量上表达目标公式来重用它们是很好的
然而,有一个很好的选项来实现第二个选项,它看起来非常漂亮:
std::vector<int> a, b, c, d, e;
// fill a, b, c, d with data
auto expression = [](int a, int b, int c, int d){ return a * b + c * d; };
assert (a.size() == b.size() && b.size() == c.size() && c.size() == d.size());
e.reserve(a.size());
for(auto _a = a.begin(), _b = b.begin(), _c = c.begin(), _d = d.begin(), _e = e.begin();
_a != a.end();
++_a, ++_b, ++_c, ++_d, ++_e)
{
*_e = expression(*_a, *_b, *_c, *_d);
}
通过这种方式,您可以将表达式从逻辑中分离出来进行评估:
void componentWise4(std::function<int(int,int,int,int)> f,
const std::vector<int> & a,
const std::vector<int> & b,
const std::vector<int> & c,
const std::vector<int> & d,
std::vector<int> & result)
{
assert (a.size() == b.size() && b.size() == c.size() && c.size() == d.size());
result.reserve(a.size());
for(auto _a = a.begin(), _b = b.begin(), _c = c.begin(), _d = d.begin(), _result = result.begin();
_a != a.end();
++_a, ++_b, ++_c, ++_d, ++_result)
{
*_result = expression(*_a, *_b, *_c, *_d);
}
}
这就是所谓的
std::vector<int> a, b, c, d, e;
// fill a, b, c, d with data
componentWise4([](int a, int b, int c, int d){ return a * b + c * d; },
a, b, c, d, e);
我相信这个"表达式计算器"可以使用C++11的新功能"可变模板"进行扩展,以支持表达式中任意数量的参数,甚至支持不同的类型。我无法让它工作(可变模板的事情),你可以尝试在这里完成我的尝试:http://ideone.com/w88kuG(我是变元模板的新手,所以我不知道语法)。
您想要的是"C++编程语言"Bjarne Stroustrup的第三版,22.4.7临时、复制和循环[num.matrix]。这本书总是个好主意。
如果你没有,基本上我们有两个选择:
首先:我们编写了一组函数,用于直接计算一些最期望的组合(例如mul_add_and_assign(&U,&M,&V,&W)来计算U=M*V+W),并引导用户自己选择他最方便的函数。
第二:我们可以引入一些辅助类(例如VxV
、VplusV
等),它们只保留对每个操作的参数的引用,并定义到vector
的运算符转换。现在,我们创建运算符+
和*
的重载,它们通过引用获取两个向量,并只返回相应类型的对象。我们可以创建VxVplusVxV
类型的类来计算更复杂的运算。现在我们可以将operator=
过载到将VxVplusVxV
初始化到vector
。在上一次重载中,我们使用对辅助类对象中保留的参数的引用进行了所有计算,没有创建临时向量或创建了最小的临时向量。
- 为什么比较运算符如此快速
- C++映射:具有自定义类的运算符[]不起作用(总是返回0)
- 使用C++中的模板和运算符重载执行矩阵运算
- 为什么这个运算符<重载函数对 STL 算法不可见?
- 增量运算符与后缀混淆
- 实现无开销push_back的最佳方法是什么
- 一个关于在C++中重载布尔运算符的问题
- 运算符C++ "delete []"仅删除 2 个前值
- 模板类无法识别友元运算符
- 我可以使用条件运算符初始化C风格的字符串文字吗
- 关闭||运算符优化
- 通过继承类使用来自不同命名空间的运算符
- C++Cast运算符过载
- 如何使用AngelScript注册SFML Vector2运算符
- 重载元组索引运算符-C++
- 如何使用重载的相等(==)运算符向测试用例添加描述
- 一组值的零开销下标运算符
- 自定义运算符重载C++,无开销
- 重载运算符 [] 从 1 开始,并产生性能开销
- C++数组运算符开销