【发布时间】:2012-06-13 05:13:48
【问题描述】:
我有一些代码可以遍历(多变量)数值范围:
#include <array>
#include <limits>
#include <iostream>
#include <iterator>
template <int N>
class NumericRange : public std::iterator<double, std::input_iterator_tag>
{
public:
NumericRange() {
_lower.fill(std::numeric_limits<double>::quiet_NaN());
_upper.fill(std::numeric_limits<double>::quiet_NaN());
_delta.fill(std::numeric_limits<double>::quiet_NaN());
}
NumericRange(const std::array<double, N> & lower, const std::array<double, N> & upper, const std::array<double, N> & delta):
_lower(lower), _upper(upper), _delta(delta) {
_state.fill(std::numeric_limits<double>::quiet_NaN());
}
const std::array<double, N> & get_state() const {
return _state;
}
NumericRange<N> begin() const {
NumericRange<N> result = *this;
result.start();
return result;
}
NumericRange<N> end() const {
NumericRange<N> result = *this;
result._state = _upper;
return result;
}
bool operator !=(const NumericRange<N> & rhs) const {
return in_range();
// return ! (*this == rhs);
}
bool operator ==(const NumericRange<N> & rhs) const {
return _state == rhs._state && _lower == rhs._lower && _upper == rhs._upper && _delta == rhs._delta;
}
const NumericRange<N> & operator ++() {
advance();
if ( ! in_range() )
_state = _upper;
return *this;
}
const std::array<double, N> & operator *() const {
return _state;
}
void start() {
_state = _lower;
}
bool in_range(int index_to_advance = N-1) const {
return ( _state[ index_to_advance ] - _upper[ index_to_advance ] ) < _delta[ index_to_advance ];
}
void advance(int index_to_advance = 0) {
_state[ index_to_advance ] += _delta[ index_to_advance ];
if ( ! in_range(index_to_advance) ) {
if (index_to_advance < N-1) {
// restart index_to_advance
_state[index_to_advance] = _lower[index_to_advance];
// carry
++index_to_advance;
advance(index_to_advance);
}
}
}
private:
std::array<double, N> _lower, _upper, _delta, _state;
};
int main() {
std::array<double, 7> lower{{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}};
std::array<double, 7> upper{{1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}};
std::array<double, 7> delta{{0.03, 0.06, 0.03, 0.06, 0.03, 0.06, 0.03}};
NumericRange<7> nr(lower, upper, delta);
int c = 0;
for (nr.start(); nr.in_range(); nr.advance()) {
++c;
}
std::cout << "took " << c << " steps" << std::endl;
return 0;
}
使用g++ -std=c++11 -O3(或使用 gcc -std=c++0x)在我的计算机上运行大约 13.8 秒。
如果我将 main 函数更改为使用基于范围的 for 循环:
for (const std::array<double, 7> & arr : nr) {
++c;
}
运行时间增加到 29.8 秒。巧合的是,这约 30 秒的运行时间与使用 std::vector<double> 而不是 std::array<double, N> 时的原始运行时间几乎相同,这让我相信编译器无法展开范围生成的代码-基于for循环。
有没有办法在保持原始速度的同时仍然使用基于范围的 for 循环?
我的尝试:
通过更改NumericRange 中的两个成员函数,我可以使用基于范围的for 循环获得所需的速度:
bool operator !=(const NumericRange<N> & rhs) const {
return in_range();
// return ! (*this == rhs);
}
const NumericRange<N> & operator ++() {
advance();
// if ( ! in_range() )
// _state = _upper;
return *this;
}
但是,这段代码感觉设计得很糟糕,因为!= operator 不能按预期工作 通常对于数字运算,我使用< 来终止循环而不是==。我想找到第一个超出范围的值,但由于数值错误,分析上这样做可能无法给出准确的答案。
如何强制 != operator 表现得与 < 相似而不误导其他会看到我的代码的人?我只需将 begin() 和 end() 函数设为私有,但对于基于范围的 for 循环,它们需要公开。
非常感谢您的帮助。
【问题讨论】:
-
您的
begin和end实现非常昂贵。 -
@ildjarn: 这应该没问题,因为它们只被调用一次。
-
@KarolyHorvath 肯定是这样。我真的很想成为一个更好的程序员,并且对像你这样批评但不提供任何帮助的 cmets 感到灰心。您认为您可以通过解释改进我的代码和设计的方法来提供帮助吗?
-
可能是一个愚蠢的建议..但是您的
end不会为循环的每次迭代调用吗?这会阻碍性能。 -
@Asha:不,range-for 语法保证它只会被调用一次。只是
operator++和operator==太贵了。
标签: c++ optimization iterator c++11