¿Hay una clase de rango en C++11 para usar con bucles for basados en rango?


Me encontré escribiendo esto hace un poco:

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

Y esto me permite escribir cosas como esta:

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

Ahora, sé que lo que escribí tal vez no sea el mejor código. Y tal vez haya una manera de hacerlo más flexible y útil. Pero me parece que algo como esto debería haber sido parte del estándar.

Así es? ¿Se añadió algún tipo de nueva biblioteca para iteradores sobre un rango de enteros, o tal vez un rango genérico de valores escalares calculados?

Author: Xeo, 2011-08-25

8 answers

La biblioteca estándar de C++ no tiene uno, pero Boost.Range tiene boost:: counting_range, que ciertamente califica. También puedes usar boost::irange, que está un poco más enfocado en el alcance.

 53
Author: Nicol Bolas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-25 06:09:07

Hasta donde yo sé, no existe tal clase en C++11.

De todos modos, traté de mejorar su implementación. Lo hice sin plantilla , ya que no veo ninguna ventaja al hacerlo plantilla . Por el contrario, tiene una desventaja importante: que no puede crear el rango en tiempo de ejecución, ya que necesita conocer los argumentos de la plantilla en tiempo de compilación.

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

Aquí está el código:

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

Código de prueba:

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

Salida:

10 11 12 13 14 15 16 17 18 19

Onine demo .

 42
Author: Nawaz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-17 16:07:49

Escribí una biblioteca llamada range para exactamente el mismo propósito excepto que es un rango de tiempo de ejecución, y la idea en mi caso vino de Python. Consideré una versión en tiempo de compilación, pero en mi humilde opinión no hay ninguna ventaja real para obtener la versión en tiempo de compilación. Puede encontrar la biblioteca en bitbucket, y está bajo licencia Boost: Intervalo. Es una biblioteca de un solo encabezado, compatible con C++03 y funciona como charm con range-based for loops en C++11 :)

Características :

  • Un verdadero contenedor de acceso aleatorio con todas las campanas y silbatos!

  • Los rangos se pueden comparar lexicográficamente.

  • Dos funciones exist(devuelve bool), y find (devuelve el iterador) para comprobar la existencia de un número.

  • La biblioteca se prueba unitariamente usando CATCH.

  • Ejemplos de basic uso, trabajar con contenedores estándar, trabajar con estándar algoritmo y trabajar con range based for loops.

He aquí una introducción de un minuto. Por último, acojo con satisfacción cualquier sugerencia sobre esta pequeña biblioteca.

 11
Author: AraK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-28 08:47:17

Encontré que boost::irange era mucho más lento que el bucle entero canónico. Así que me decidí por la siguiente solución mucho más simple utilizando una macro preprocesador:

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

Entonces puedes hacer loop así:

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

Este rango comienza automáticamente desde cero. Se podría extender fácilmente a partir de un número dado.

 3
Author: user2664470,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-11 10:49:08

Aquí hay una forma más simple que está funcionando muy bien para mí. ¿Hay algún riesgo en mi enfoque?

r_iterator es un tipo que se comporta, tanto como sea posible, como un long int. Por lo tanto, muchos operadores como == y ++, simplemente pasan a través del long int. 'Expongo' el long int subyacente a través de las conversiones operator long int y 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;
}

(Editar: - podemos hacer los métodos de range estáticos en lugar de const.)

 2
Author: Aaron McDaid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-20 15:55:54

Esto puede ser un poco tarde, pero acabo de ver esta pregunta y he estado usando esta clase desde hace un tiempo:

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

Uso:

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;
}
 1
Author: OneOfOne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-11 22:43:49

¿Ha intentado usar

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

La mayoría de las veces se ajusta a la cuenta.

Por ejemplo

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

}

Tenga en cuenta que printInt puede ser reemplazado por un lambda en C++0x. También una pequeña variación más de este uso podría ser (estrictamente para random_iterator)

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

Solo para iterador Fwd

 for_each(advance(v.begin(),5),advance(v.begin(),10),printInt);
 0
Author: Ajeet Ganga,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-25 06:05:44

Puede generar fácilmente una secuencia creciente en C++11 usando 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;
}
 -1
Author: blue scorpion,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-25 22:42:50