365 votes

C++11 : boucle for inversée basée sur l'intervalle.

Existe-t-il un adaptateur de conteneur qui inverserait le sens des itérateurs afin que je puisse itérer sur un conteneur en sens inverse avec un for-loop basé sur la plage ?

Avec des itérateurs explicites, je convertirais ceci :

for (auto i = c.begin(); i != c.end(); ++i) { ...

dans ceci :

for (auto i = c.rbegin(); i != c.rend(); ++i) { ...

Je veux convertir ça :

for (auto& i: c) { ...

à ça :

for (auto& i: std::magic_reverse_adapter(c)) { ...

Cela existe-t-il ou dois-je l'écrire moi-même ?

19 votes

Un adaptateur de conteneur inversé, cela semble intéressant, mais je pense que vous devrez l'écrire vous-même. Nous n'aurions pas ce problème si le comité de normalisation se dépêchait d'adapter des algorithmes basés sur des plages au lieu d'itérateurs explicites.

0 votes

@Seth Je sais que je pourrais l'écrire, mais ce n'est pas la question. Si je l'écris, elle devient une de ces fonctions utilitaires qui n'ont pas leur place quelque part en particulier (tm), donc vous finissez par saupoudrer votre code avec l'inclusion d'un tel en-tête utilitaire et vous mélangez votre système de construction pour le partager entre les projets. Selon ce raisonnement, nous devrions toujours utiliser BOOST_FOREACH au lieu de range-for. Et oui, je suis paresseux.

5 votes

@deft_code : "au lieu de ?" Pourquoi voudriez-vous vous débarrasser des algorithmes basés sur les itérateurs ? Ils sont bien meilleurs et moins verbeux pour les cas où vous n'itérez pas à partir de begin a end ou pour traiter les itérateurs de flux et autres. Les algorithmes d'intervalle seraient parfaits, mais ils ne sont en fait que du sucre syntaxique (à l'exception de la possibilité d'une évaluation paresseuse) par rapport aux algorithmes d'itérateurs.

258voto

KennyTM Points 232647

En fait, Boost a un tel adaptateur : boost::adaptors::reverse .

#include <list>
#include <iostream>
#include <boost/range/adaptor/reversed.hpp>

int main()
{
    std::list<int> x { 2, 3, 5, 7, 11, 13, 17, 19 };
    for (auto i : boost::adaptors::reverse(x))
        std::cout << i << '\n';
    for (auto i : x)
        std::cout << i << '\n';
}

26voto

Paul Points 4552

Cela devrait fonctionner en C++11 sans boost :

namespace std {
template<class T>
T begin(std::pair<T, T> p)
{
    return p.first;
}
template<class T>
T end(std::pair<T, T> p)
{
    return p.second;
}
}

template<class Iterator>
std::reverse_iterator<Iterator> make_reverse_iterator(Iterator it)
{
    return std::reverse_iterator<Iterator>(it);
}

template<class Range>
std::pair<std::reverse_iterator<decltype(begin(std::declval<Range>()))>, std::reverse_iterator<decltype(begin(std::declval<Range>()))>> make_reverse_range(Range&& r)
{
    return std::make_pair(make_reverse_iterator(begin(r)), make_reverse_iterator(end(r)));
}

for(auto x: make_reverse_range(r))
{
    ...
}

72 votes

IIRC ajouter quoi que ce soit au namespace std est une invitation à l'échec épique.

38 votes

Je ne suis pas sûr de la signification normative de "epic fail", mais surcharger une fonction dans le cadre de l'initiative std a un comportement non défini selon 17.6.4.2.1.

9 votes

C'est dans C++14 apparemment sous ce nom.

11voto

Arlen Points 2967

Est-ce que ça marche pour vous :

#include <iostream>
#include <list>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/iterator_range.hpp>

int main(int argc, char* argv[]){

  typedef std::list<int> Nums;
  typedef Nums::iterator NumIt;
  typedef boost::range_reverse_iterator<Nums>::type RevNumIt;
  typedef boost::iterator_range<NumIt> irange_1;
  typedef boost::iterator_range<RevNumIt> irange_2;

  Nums n = {1, 2, 3, 4, 5, 6, 7, 8};
  irange_1 r1 = boost::make_iterator_range( boost::begin(n), boost::end(n) );
  irange_2 r2 = boost::make_iterator_range( boost::end(n), boost::begin(n) );

  // prints: 1 2 3 4 5 6 7 8 
  for(auto e : r1)
    std::cout << e << ' ';

  std::cout << std::endl;

  // prints: 8 7 6 5 4 3 2 1
  for(auto e : r2)
    std::cout << e << ' ';

  std::cout << std::endl;

  return 0;
}

5voto

Jive Dadson Points 3563

Je ne connaissais pas la version boost. J'avais déjà écrit un adaptateur qui fonctionne, lorsque je suis tombé sur cette question-réponse. Je faisais des recherches sur Stackoverflow pour voir si je pouvais comprendre pourquoi le compilateur MS n'est pas satisfait à moins que j'utilise des types de retour de fin pour begin() et end(). Voici le code qui fonctionne :

#include <string>
#include <iostream>

template<class Fwd>
struct Reverser {
    const Fwd &fwd;
    Reverser<Fwd>(const Fwd &fwd_): fwd(fwd_) {}
    auto begin() -> decltype(fwd.rbegin()) const { return fwd.rbegin(); } 
    auto end() -> decltype(fwd.rend()) const  { return fwd.rend(); } 
};

template<class Fwd>
Reverser<Fwd> reverse(const Fwd &fwd) { return Reverser<Fwd>(fwd); }

int main() {
    using namespace std;
    string str = ".dlrow olleH";
    for(char c: reverse(str)) cout << c;
    cout << endl;
}

UPDATE : En voici un meilleur.

template<class Fwd>
struct Reverser_generic {
    Fwd &fwd;
    Reverser_generic(Fwd& fwd_): fwd(fwd_) {}
    typedef std::reverse_iterator<typename Fwd::iterator> reverse_iterator;
    reverse_iterator begin() { return reverse_iterator(std::end(fwd)); } 
    reverse_iterator end() { return reverse_iterator(std::begin(fwd)); } 
};

template<class Fwd >
struct Reverser_special{
    Fwd &fwd;
    Reverser_special(Fwd& fwd_): fwd(fwd_) {}
    auto begin() -> decltype(fwd.rbegin()){ return fwd.rbegin(); } 
    auto end() ->decltype(fwd.rbegin())   { return fwd.rend(); } 
};

template<class Fwd>
auto reverse_impl(Fwd& fwd, long) -> decltype( Reverser_generic<Fwd>(fwd)){ 
    return Reverser_generic<Fwd>(fwd);
}

template<class Fwd>
auto reverse_impl(Fwd& fwd, int) 
    -> decltype(fwd.rbegin(), Reverser_special<Fwd>(fwd))
{ 
    return Reverser_special<Fwd>(fwd);
}

template<class Fwd>
auto reverse( Fwd&& fwd) -> decltype(reverse_impl(fwd,int(0))) {
    static_assert(!(is_rvalue_reference<Fwd&&>::value), 
        "Cannot pass rvalue_reference to dj::reverse()");
    return reverse_impl(fwd,int(0));
}

0voto

allyourcode Points 5670

Si vous avez une aversion pour Boost, vous pouvez le faire : https://gist.github.com/allyourcode/5eefdff686c5406220f0

J'aimerais que Iterable soit dans la librairie std.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X