web-dev-qa-db-fra.com

Existe-t-il une classe de plage en C ++ 11 à utiliser avec une plage basée sur des boucles?

Je me suis retrouvé à écrire ceci il y a un peu:

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

Et cela me permet d'écrire des choses comme ça:

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

Maintenant, je sais que ce que j'ai écrit n'est peut-être pas le meilleur code. Et peut-être qu'il existe un moyen de le rendre plus flexible et utile. Mais il me semble que quelque chose comme ça aurait dû faire partie de la norme.

Donc est-il? Une nouvelle bibliothèque a-t-elle été ajoutée pour les itérateurs sur une plage d'entiers, ou peut-être une plage générique de valeurs scalaires calculées?

97
Omnifarious

La bibliothèque standard C++ n'en a pas, mais Boost.Range a boost :: counting_range , ce qui est certainement admissible. Vous pouvez également utiliser boost :: irange , qui est un peu plus ciblé.

La bibliothèque de plages de C++ 20 vous permettra de le faire via view::iota(start, end).

55
Nicol Bolas

Pour autant que je sache, il n'y a pas une telle classe en C++ 11.

Quoi qu'il en soit, j'ai essayé d'améliorer votre implémentation. Je l'ai fait non-template , car je ne vois aucun avantage en le créant modèle . Au contraire, cela présente un inconvénient majeur: vous ne pouvez pas créer la plage au moment de l'exécution, car vous devez connaître les arguments du modèle au moment de la compilation.

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

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

Code de test:

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

Sortie:

10 11 12 13 14 15 16 17 18 19

démo Onine .

46
Nawaz

J'ai écrit une bibliothèque appelée range dans le même but, sauf qu'il s'agit d'une plage d'exécution, et l'idée dans mon cas est venue de Python. J'ai envisagé une version au moment de la compilation, mais à mon humble avis, il n'y a pas vraiment d'avantage à acquérir la version au moment de la compilation. Vous pouvez trouver la bibliothèque sur bitbucket, et elle est sous licence Boost: Range. C'est une bibliothèque à un en-tête, compatible avec C++ 03 et fonctionne comme un charme avec des boucles basées sur une plage en C++ 11 :)

Caractéristiques :

  • Un vrai conteneur à accès aléatoire avec toutes les cloches et les sifflets!

  • Les plages peuvent être comparées lexicographiquement.

  • Deux fonctions exist (retourne bool) et find (retourne itérateur) pour vérifier l'existence d'un nombre.

  • La bibliothèque est testée en utilisant CATCH .

  • Exemples d'utilisation de base, utilisation de conteneurs standard, utilisation d'algorithmes standard et utilisation d'une plage basée sur des boucles.

Voici une introduction d'une minute . Enfin, je me réjouis de toute suggestion concernant cette petite bibliothèque.

12
AraK

Je l'ai trouvé boost::irange était beaucoup plus lent que la boucle d'entier canonique. J'ai donc opté pour la solution beaucoup plus simple suivante à l'aide d'une macro de préprocesseur:

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

Ensuite, vous pouvez boucler comme ceci:

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

Cette plage démarre automatiquement à partir de zéro. Il pourrait être facilement étendu pour commencer à partir d'un nombre donné.

4
user2664470

Voici un formulaire plus simple qui fonctionne bien pour moi. Y a-t-il des risques dans mon approche?

r_iterator est un type qui se comporte, autant que possible, comme un long int. Par conséquent, de nombreux opérateurs tels que == et ++, passez simplement au long int. J'expose le long int sous-jacent via le operator long int et 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: - nous pouvons rendre les méthodes de range statiques au lieu de const.)

2
Aaron McDaid

C'est peut-être un peu tard, mais je viens de voir cette question et j'utilise ce cours depuis un moment maintenant:

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

Utilisation:

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
OneOfOne

as-tu essayé d'utiliser

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

La plupart du temps, ça va.

Par exemple.

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

}

Notez que printInt peut remplacer OFC par un lambda en C++ 0x. Une autre petite variation de cette utilisation pourrait également être (strictement pour random_iterator)

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

Pour l'itérateur Fwd uniquement

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