Is there a range class in C++11 for use with range based for loops?

前端 未结 8 1252
失恋的感觉
失恋的感觉 2020-11-27 11:33

I found myself writing this just a bit ago:

template 
class range_class {
 public:
   class iterator {
      friend c         


        
相关标签:
8条回答
  • 2020-11-27 11:42

    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.

    0 讨论(0)
  • 2020-11-27 11:42

    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;
    }
    
    0 讨论(0)
  • 2020-11-27 11:47

    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.

    0 讨论(0)
  • 2020-11-27 11:48

    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;
    }
    
    0 讨论(0)
  • 2020-11-27 11:51

    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);
    
    0 讨论(0)
  • 2020-11-27 11:53

    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.)

    0 讨论(0)
提交回复
热议问题