C + + 11中是否有用于基于 range for 循环的 range 类?

我不久前才发现自己在写这个:

template <long int T_begin, long int T_end>
class range_class {
public:
class iterator {
friend class range_class;
public:
long int operator *() const { return i_; }
const iterator &operator ++() { ++i_; return *this; }
iterator operator ++(int) { iterator copy(*this); ++i_; return copy; }


bool operator ==(const iterator &other) const { return i_ == other.i_; }
bool operator !=(const iterator &other) const { return i_ != other.i_; }


protected:
iterator(long int start) : i_ (start) { }


private:
unsigned long i_;
};


iterator begin() const { return iterator(T_begin); }
iterator end() const { return iterator(T_end); }
};


template <long int T_begin, long int T_end>
const range_class<T_begin, T_end>
range()
{
return range_class<T_begin, T_end>();
}

这让我可以这样写:

for (auto i: range<0, 10>()) {
// stuff with i
}

我知道我写的可能不是最好的代码。也许有办法让它更灵活更实用。但在我看来,像这样的东西应该成为标准的一部分。

是吗?是为整数范围内的迭代器添加了某种新库,还是为计算标量值的通用范围添加了某种新库?

55230 次浏览

As far as I know, there is no such class in C++11.

Anyway, I tried to improve your implementation. I made it non-template, as I don't see any advantage in making it template. On the contrary, it has one major disadvantage : that you cannot create the range at runtime, as you need to know the template arguments at compile time itself.

//your version
auto x = range<m,n>(); //m and n must be known at compile time


//my version
auto x = range(m,n);  //m and n may be known at runtime as well!

Here is the code:

class range {
public:
class iterator {
friend class range;
public:
long int operator *() const { return i_; }
const iterator &operator ++() { ++i_; return *this; }
iterator operator ++(int) { iterator copy(*this); ++i_; return copy; }


bool operator ==(const iterator &other) const { return i_ == other.i_; }
bool operator !=(const iterator &other) const { return i_ != other.i_; }


protected:
iterator(long int start) : i_ (start) { }


private:
unsigned long i_;
};


iterator begin() const { return begin_; }
iterator end() const { return end_; }
range(long int  begin, long int end) : begin_(begin), end_(end) {}
private:
iterator begin_;
iterator end_;
};

Test code:

int main() {
int m, n;
std::istringstream in("10 20");
if ( in >> m >> n ) //using in, because std::cin cannot be used at coliru.
{
if ( m > n ) std::swap(m,n);
for (auto i : range(m,n))
{
std::cout << i << " ";
}
}
else
std::cout <<"invalid input";
}

Output:

10 11 12 13 14 15 16 17 18 19

Onine demo.

have you tried using

template <class InputIterator, class Function>
Function for_each (InputIterator first, InputIterator last, Function f);

Most of the time fits the bill.

E.g.

template<class T> void printInt(T i) {cout<<i<<endl;}
void test()
{
int arr[] = {1,5,7};
vector v(arr,arr+3);


for_each(v.begin(),v.end(),printInt);


}

Note that printInt can OFC be replaced with a lambda in C++0x. Also one more small variation of this usage could be (strictly for random_iterator)

 for_each(v.begin()+5,v.begin()+10,printInt);

For Fwd only iterator

 for_each(advance(v.begin(),5),advance(v.begin(),10),printInt);

The C++ standard library does not have one, but Boost.Range has boost::counting_range, which certainly qualifies. You could also use boost::irange, which is a bit more focused in scope.

C++20's range library will allow you to do this via view::iota(start, end).

I wrote a library called range for exactly the same purpose except it is a run-time range, and the idea in my case came from Python. I considered a compile-time version, but in my humble opinion there is no real advantage to gain out the compile-time version. You can find the library on bitbucket, and it is under Boost License: Range. It is a one-header library, compatible with C++03 and works like charm with range-based for loops in C++11 :)

Features:

  • A true random access container with all the bells and whistles!

  • Ranges can be compared lexicographically.

  • Two functions exist(returns bool), and find(returns iterator) to check the existence of a number.

  • The library is unit-tested using CATCH.

  • Examples of basic usage, working with standard containers, working with standard algorithms and working with range based for loops.

Here is a one-minute introduction. Finally, I welcome any suggestion about this tiny library.

Here is a simpler form which is working nicely for me. Are there any risks in my approach?

r_iterator is a type which behaves, as much as possible, like a long int. Therefore many operators such as == and ++, simply pass through to the long int. I 'expose' the underlying long int via the operator long int and operator long int & conversions.

#include <iostream>
using namespace std;


struct r_iterator {
long int value;
r_iterator(long int _v) : value(_v) {}
operator long int () const { return value; }
operator long int& ()      { return value; }
long int operator* () const { return value; }
};
template <long int _begin, long int _end>
struct range {
static r_iterator begin() {return _begin;}
static r_iterator end  () {return _end;}
};
int main() {
for(auto i: range<0,10>()) { cout << i << endl; }
return 0;
}

(Edit: - we can make the methods of range static instead of const.)

This might be a little late but I just saw this question and I've been using this class for a while now :

#include <iostream>
#include <utility>
#include <stdexcept>


template<typename T, bool reverse = false> struct Range final {
struct Iterator final{
T value;
Iterator(const T & v) : value(v) {}
const Iterator & operator++() { reverse ? --value : ++value; return *this; }
bool operator!=(const Iterator & o) { return o.value != value; }
T operator*() const { return value; }
};
T begin_, end_;
Range(const T & b, const T & e)  : begin_(b), end_(e) {
if(b > e) throw std::out_of_range("begin > end");
}


Iterator begin() const { return reverse ? end_ -1 : begin_; }
Iterator end() const { return reverse ? begin_ - 1: end_; }


Range() = delete;
Range(const Range &) = delete;
};


using UIntRange = Range<unsigned, false>;
using RUIntRange = Range<unsigned, true>;

Usage :

int main() {
std::cout << "Reverse : ";
for(auto i : RUIntRange(0, 10)) std::cout << i << ' ';
std::cout << std::endl << "Normal : ";
for(auto i : UIntRange(0u, 10u)) std::cout << i << ' ';
std::cout << std::endl;
}

I found that boost::irange was much slower than the canonical integer loop. So I settled on the following much simpler solution using a preprocessor macro:

#define RANGE(a, b) unsigned a=0; a<b; a++

Then you can loop like this:

for(RANGE(i, n)) {
// code here
}

This range automatically starts from zero. It could be easily extended to start from a given number.

You can easily generate an increasing sequence in C++11 using std::iota():

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>


template<typename T>
std::vector<T> range(T start, T end)
{
std::vector<T> r(end+1-start, T(0));
std::iota(r.begin(), r.end(), T(start));//increasing sequence
return r;
}


int main(int argc, const char * argv[])
{
for(auto i:range<int>(-3,5))
std::cout<<i<<std::endl;


return 0;
}