10 votes

Le balayage de boucle basé sur la plage peut-il fonctionner sur une plage

Si j'ai une plage (une paire de 2 itérateurs), y a-t-il un moyen d'écrire une boucle "pour chaque" qui utilise la plage, et non un tableau brut ou un conteneur.

Quelque chose comme ceci:

auto rng = std::equal_range(v.begin(),v.end(),1984);
pour(const auto& elem: rng) {
    // ...
}

15voto

ecatmur Points 64173

Tel que mentionné dans Pourquoi l'accès à la plage de paires a été supprimé de C++11?, vous pouvez utiliser un adapteur par exemple le as_range de la réponse acceptée, boost::make_iterator_range, ou écrire le vôtre :

template struct range {
   It begin_, end_;
   It begin() const { return begin_; }
   It end() const { return end_; }
};
template range as_range(const std::pair &p) {
   return {p.first, p.second};
}

auto rng = std::equal_range(v.begin(),v.end(),1984);
for(const auto& elem: as_range(rng))
    ...

La raison pour laquelle cela ne peut pas être appliqué de manière générale est que conformément au document de Alastair Meredith, parmi les algorithmes,

  • mismatch et partition_copy retournent une paire d'itérateurs provenant de plages différentes;
  • minmax retourne une paire d'objets qui ne sont peut-être pas des itérateurs du tout, et s'ils le sont, il n'y a aucune garantie qu'ils forment une plage;
  • minmax_element peut retourner une plage, mais il peut également retourner une plage inversée (par exemple, sur une plage triée en ordre décroissant, minmax_element retournera {prev(last), first};
  • equal_range est garanti de retourner une plage.

3voto

Ivaylo Strandjev Points 38924

Je ne pense pas que cela fonctionnera comme ça sorti de la boîte car equal_range renvoie une paire d'itérateurs alors que la boucle for sur la plage selon la documentation est :

Les expressions begin_expr et end_expr sont définies comme suit :
Si (__range) est un tableau, alors (__range) et (__range + __bound), où __bound est la limite du tableau
Si (__range) est une classe et possède soit un membre begin, soit un membre end (ou les deux), alors begin_expr est __range.begin() et end_expr est __range.end();
Sinon, begin(__range) et end(__range), qui sont trouvés en fonction des règles de recherche dépendantes des arguments avec std en tant que namespace associé.

Je dirais que vous pourriez définir des fonctions begin et end qui prennent la paire d'itérateurs et renvoient respectivement le premier et le deuxième.

1voto

#include 
#include 
#include 

template 
struct range_adapter {
    std::pair p;

    range_adapter(const std::pair &p) : p(p) {}

    I begin() const { return p.first; }
    I end() const { return p.second; }
};

template 
range_adapter in_range(const std::pair &p)
{
    return range_adapter(p);
}

int main()
{
    std::vector data { 1, 2, 2, 3, 3, 3, 4 };

    auto r = std::equal_range(data.begin(), data.end(), 2);

    for (const auto &elem : in_range(r))
    {
        std::cout << elem << std::endl;
    }
}

0voto

Ed Rowlett-Barbu Points 1489

Ce que std::equal_range renvoie est simplement un std::pair. La norme ne couvre aucune méthode pour itérer sur de telles choses.

Ce que vous voudrez peut-être lire est la présentation d'Alexandrescu "Les itérateurs doivent disparaître". Voici la vidéo. Une excellente lecture sur une manière plus élégante d'itérer sur des conteneurs en utilisant des Plages.

Les Plages sont implémentées dans sa bibliothèque Loki.

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