Подтвердить что ты не робот

Существует ли класс диапазона в С++ 11 для использования с диапазоном, основанным на циклах?

Я понял, что писал это немного назад:

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
}

Теперь я знаю, что я написал, возможно, не лучший код. И, возможно, есть способ сделать его более гибким и полезным. Но мне кажется, что что-то подобное должно было стать частью стандарта.

Так оно и есть? Была ли какая-то новая библиотека добавлена ​​для итераторов по целому ряду целых чисел или, может быть, к общему числу вычисленных скалярных значений?

4b9b3361

Ответ 1

Стандартная библиотека C++ не имеет таковой, но Boost.Range имеет boost :: counting_range, что, безусловно, подходит. Вы также можете использовать boost :: irange, который немного более сфокусирован.

C++ 20 библиотека диапазонов позволит вам сделать это через view::iota(start, end).

Ответ 2

Насколько я знаю, такого класса нет в С++ 11.

В любом случае, я попытался улучшить вашу реализацию. Я сделал его не шаблоном, так как я не вижу никакого преимущества в создании шаблона. Напротив, у него есть один главный недостаток: вы не можете создать диапазон во время выполнения, так как вам нужно знать аргументы шаблона во время компиляции.

//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!

Вот код:

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_;
};

Тестовый код:

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";
}

Вывод:

10 11 12 13 14 15 16 17 18 19

Onine demo.

Ответ 3

Я написал библиотеку с именем range для точно такой же цели, за исключением того, что это диапазон времени выполнения, и идея в моем случае исходила из Python. Я считал версию с компиляцией, но по моему скромному мнению нет реального преимущества для получения версии времени компиляции. Вы можете найти библиотеку на битбакете, и она находится под лицензией Boost: Range. Это библиотека с одним заголовком, совместимая с С++ 03 и работающая как прелесть с циклами на основе циклов в С++ 11:)

Особенности:

  • Настоящий контейнер с произвольным доступом со всеми колокольчиками!

  • Диапазоны можно сравнивать лексикографически.

  • Две функции exist (возвращает bool) и find (возвращает итератор), чтобы проверить наличие числа.

  • Библиотека протестирована с помощью CATCH.

  • Примеры основных использование, работа со стандартными контейнерами, работа со стандартными алгоритмы и работа с диапазоном, основанным на циклах.

Вот одноминутное введение. Наконец, я приветствую любое предложение об этой крошечной библиотеке.

Ответ 4

Я обнаружил, что boost::irange был намного медленнее канонического целочисленного цикла. Поэтому я остановился на следующем гораздо более простом решении, используя макрос препроцессора:

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

Затем вы можете сделать так:

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

Этот диапазон автоматически начинается с нуля. Его можно легко расширить, чтобы начать с заданного числа.

Ответ 5

Вот более простая форма, которая хорошо работает для меня. Существуют ли какие-либо риски в моем подходе?

r_iterator - это тип, который ведет себя как можно больше, как a long int. Поэтому многие операторы, такие как == и ++, просто переходят к long int. Я "выставляю" базовый длинный int через преобразования operator long int и operator long int &.

#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;
}

( Изменить: - мы можем создавать методы range static вместо const.)

Ответ 6

Это может быть немного поздно, но я только что увидел этот вопрос, и я использовал этот класс некоторое время сейчас:

#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>;

Использование:

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;
}

Ответ 7

Вы пытались использовать

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

Большая часть времени подходит для счета.

например.

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

}

Обратите внимание, что printInt может заменить OFC на lambda в С++ 0x. Также может быть еще одна небольшая вариация этого использования (строго для random_iterator)

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

Для Fwd только итератор

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

Ответ 8

Вы можете легко генерировать возрастающую последовательность в С++ 11 с помощью 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;
}