我在类似矢量的类中使用表达式模板进行转换,例如移动平均线。在这里,与标准算术运算不同,operator[](size_t i)
不会对 element 进行单个访问i
,而是需要评估整个循环,例如对于向量的移动平均值v
double operator[](size_t i) const
{
double ad=0.0;
for(int j=i-period+1; j<=i; ++j)
ad+=v[j];
return ad/period;
}
(这不是真正的功能,因为必须关心非负索引,但现在这无关紧要)。
在使用这种移动平均线结构时,我担心代码会变得相当低效,尤其是在采用双重或三重移动平均线的情况下。然后获得嵌套循环,因此获得周期大小的二次或三次缩放。
我的问题是,编译器是否如此聪明地以某种方式优化这些冗余循环?还是不是这种情况,必须手动处理中间存储(我猜是这样)?如何在下面的示例代码中合理地做到这一点?
示例代码,改编自Wikipedia,使用 Visual Studio 2013 编译:
CRTP 基类和实际向量:
#include <vector>
template <typename E>
struct VecExpression
{
double operator[](int i) const { return static_cast<E const&>(*this)[i]; }
};
struct Vec : public VecExpression<Vec>
{
Vec(size_t N) : data(N) {}
double operator[](int i) const { return data[i]; }
double& operator[](int i) { return data[i]; }
std::vector<double> data;
};
移动平均线类:
template <typename VectorType>
struct VecMovingAverage : public VecExpression<VecMovingAverage<VectorType> >
{
VecMovingAverage(VectorType const& _vector, int _period) : vector(_vector), period(_period) {}
double operator[](int i) const
{
int s = std::max(i - period + 1, 0);
double ad = 0.0;
for (int j = s; j <= i; ++j)
ad += vector[j];
return ad / (i - s + 1);
}
VectorType const& vector;
int period;
};
template<typename VectorType>
auto MovingAverage(VectorType const& vector, int period = 10) -> VecMovingAverage<VectorType>
{
return VecMovingAverage<VectorType>(vector, period);
}
现在我上面提到的恐惧出现在这样的表达中,
Vec vec(100);
auto tripleMA= MovingAverage(MovingAverage(MovingAverage(vec,20),20),20);
std::cout << tripleMA[40] << std::endl;
我想这需要20^3
对单个 ... 进行评估operator[]
?
编辑:一个明显的解决方案是存储结果。移动std::vector<double> data
到基类,然后将移动平均类更改为(未经测试)
template <typename VectorType, bool Store>
struct VecMovingAverage : public VecExpression<VecMovingAverage<VectorType, Store> >
{
VecMovingAverage(VectorType const& _vector, int _period) : vector(_vector), period(_period) {}
double operator[](int i) const
{
if(Store && i<data.size())
{
return data[i];
}
else
{
int s = std::max(i - period + 1, 0);
double ad = 0.0;
for (int j = s; j <= i; ++j)
ad += vector[j];
ad /= (i - s + 1)
if(Store)
{
data.resize(i+1);
data[i]=ad;
}
return ad;
}
}
VectorType const& vector;
int period;
};
然后可以在函数中选择存储结果:
template<typename VectorType>
auto MovingAverage(VectorType const& vector, int period = 10) -> VecMovingAverage<VectorType>
{
static const bool Store=true;
return VecMovingAverage<VectorType, Store>(vector, period);
}
这可以扩展为仅将存储应用于多个应用程序等。