use*_*ser 15 c++ optimization iterator c++11
我有一些代码来迭代(多变量)数值范围:
#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;
}
Run Code Online (Sandbox Code Playgroud)
使用g++ -std=c++11 -O3(或-std=c++0x使用gcc <4.7)编译在我的计算机上运行大约13.8秒.
如果我更改main函数以使用基于范围的for循环:
for (const std::array<double, 7> & arr : nr) {
++c;
}
Run Code Online (Sandbox Code Playgroud)
运行时间增加到29.8秒.巧合的是,这个~30秒的运行时与使用时的运行时几乎相同,std::vector<double>而不是std::array<double, N>让我相信编译器无法展开基于范围的for循环产生的代码.
有没有办法获得原始的速度,仍然使用基于范围的for循环?
我尝试过的:
通过更改以下两个成员函数,我可以使用基于范围的for循环获得所需的速度NumericRange:
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;
}
Run Code Online (Sandbox Code Playgroud)
但是,这段代码感觉设计不合理,因为!= operator它没有按预期工作.通常对于数值运算,我<用来终止循环而不是==.我想找到第一个超出范围的值,但是由于数值误差,分析可能无法给出确切的答案.
你怎么能强迫它!= operator表现得像一个<没有误导别人谁会看到我的代码?我只是将begin()和end()函数设为私有,但它们需要公开用于基于范围的for循环.
非常感谢你的帮助.
Mat*_* M. 13
就我而言,问题在于你没有适当地使用范围构造.
让我们退后一步:
void foo(std::vector<int> const& v) {
for (int i: v) {
}
}
Run Code Online (Sandbox Code Playgroud)
注意range-for如何迭代向量以提取整数.
对于您选择某些原因没有实现迭代器从桥梁begin到end,而是重新使用你迭代什么副本,即使它只是变化非常轻微,而你正在做吨的额外的工作(在副本并检查)...
注意:std::iterator<double, ...>意味着operator*应该返回一个double&.
如果你想使用新的习语,你必须符合它的期望.
期望是您使用迭代器进行迭代,而不是一遍又一遍地复制原始对象(略微修改).这是C++习语.
这意味着你需要将对象切成两半:在迭代过程中迭代所有迭代的东西以及迭代器中修改的东西.
从我所看到的:
_lower,_upper并且_delta是固定的_state 是迭代变量因此,您将拥有:
template <typename> class NumericRangeIterator
template <unsigned N> // makes no sense having a negative here
class NumericRange {
public:
template <typename> friend class NumericRangeIterator;
typedef NumericRangeIterator<NumericRange> iterator;
typedef NumericRangeIterator<NumericRange const> const_iterator;
static unsigned const Size = N;
// ... constructors
iterator begin(); // to be defined after NumericRangeIterator
iterator end();
const_iterator begin() const;
const_iterator end() const;
private:
std::array<double, N> _lower, _upper, _delta;
}; // class NumericRange
template <typename T>
class NumericRangeIterator: public
std::iterator< std::array<double, T::Size>,
std::forward_iterator_tag >
{
public:
template <unsigned> friend class NumericRange;
NumericRangeIterator(): _range(0), _state() {}
NumericRangeIterator& operator++() {
this->advance();
return *this;
}
NumericRangeIterator operator++(int) {
NumericRangeIterator tmp(*this);
++*this;
return tmp;
}
std::array<double, T::Size> const& operator*() const {
return _state;
}
std::array<double, T::Size> const* operator->() const {
return _state;
}
bool operator==(NumericRangeIterator const& other) const {
return _state != other._state;
}
bool operator!=(NumericRangeIterator const& other) const {
return !(*this == other);
}
private:
NumericRangeIterator(T& t, std::array<double, T::Size> s):
_range(&t), _state(s) {}
void advance(unsigned index = T::Size - 1); // as you did
void in_range(unsigned index = T::Size - 1); // as you did
T* _range;
std::array<double, T::Size> _state;
}; // class NumericRangeIterator
template <unsigned N>
auto NumericRange<N>::begin() -> typename NumericRange<N>::iterator {
return iterator(*this, _lower);
}
template <unsigned N>
auto NumericRange<N>::end() -> typename NumericRange<N>::iterator {
return iterator(*this, _upper);
}
Run Code Online (Sandbox Code Playgroud)
通过所有这些设置,您可以编写:
for (auto const& state: nr) {
}
Run Code Online (Sandbox Code Playgroud)
哪里auto会被推断出来std::array<double, nr::Size>.
注意:不确定它iterator是否有用,可能只是const_iterator因为它在某种程度上是错误的迭代; 你无法通过迭代器进入范围对象来修改它.
编辑: operator==太慢了,如何让它变得更好?
我建议作弊.
1 /修改迭代器的构造函数
NumericRangeIterator(): _range(0), _state() {} // sentinel value
NumericRangeIterator(T& t): _range(&t), _state(t._lower) {}
Run Code Online (Sandbox Code Playgroud)
2 /调整迭代以在最后创建新的"sentinel"值
void advance() {
// ...
if (not this->in_range()) { // getting out of the iteration ?
*this = NumericRangeIterator(); // then use the sentinel value
}
}
Run Code Online (Sandbox Code Playgroud)
3 /相应地更改begin和end定义
template <unsigned N>
auto NumericRange<N>::begin() -> typename NumericRange<N>::iterator {
return iterator(*this);
}
template <unsigned N>
auto NumericRange<N>::end() -> typename NumericRange<N>::iterator {
return iterator();
}
Run Code Online (Sandbox Code Playgroud)
4/==使用哨兵使其更加平等
bool operator==(NumericRangeIterator const& other) const {
return _range == other._range and _state == other._state;
}
Run Code Online (Sandbox Code Playgroud)
现在,一直迭代它==被短路,因为其中一个_range是空的而另一个不是.只有在最后一次调用时才会实际比较这两个_state属性.
| 归档时间: |
|
| 查看次数: |
2233 次 |
| 最近记录: |