Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

C++ : apprendre à filtrer les données d'un conteneur pendant une itération via « ranged-based for loop »
Un billet de Bousk

Le , par Bousk

0PARTAGES

Les ranged-base for loop apparues en C++11 sont un excellent moyen d'itérer sur l'ensemble des données d'un conteneur.
Mais si l'on veut ne traiter que certaines entrées selon un critère/filtre, il faut alors ajouter le filtre dans la boucle afin de ne pas exécuter l'opération sur cette entrée, ce qui n'est pas très élégant.
En attendant une solution du langage, possiblement via les ranged, dans une version future, voici un moyen relativement simple de filtrer dans la boucle, en C++11 uniquement :
Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <functional> 
template<class Container> 
class ForRangedLoopFilter 
{ 
    using value_type = typename Container::value_type; 
    using iterator = typename Container::iterator; 
  
public: 
    friend class Iterator; 
    class Iterator 
    { 
    public: 
        Iterator(ForRangedLoopFilter& owner) 
            : mOwner(owner) 
        { 
            mIterator = first(); 
        } 
        Iterator(ForRangedLoopFilter& owner, bool) 
            : mOwner(owner) 
        { 
            mIterator = last(); 
        } 
  
        value_type& operator*() { return *mIterator; } 
        value_type& operator->() { return *mIterator; } 
  
        Iterator& operator++() { mIterator = next(); return *this; } 
  
        bool operator !=(const Iterator& other) const { return mIterator != other.mIterator; } 
  
    private: 
        iterator first() 
        { 
            mIterator = mOwner.mContainer.begin(); 
            if (!mOwner.mFilterFunction(*mIterator)) 
                mIterator = next(); 
            return mIterator; 
        } 
        iterator next() 
        { 
            do { 
                mIterator++; 
            } while (mIterator != last() && !mOwner.mFilterFunction(*mIterator)); 
            return mIterator; 
        } 
        iterator last() { return mOwner.mContainer.end(); } 
  
    private: 
        ForRangedLoopFilter& mOwner; 
        iterator mIterator; 
    }; 
  
public: 
    template<class FilterFunc> 
    ForRangedLoopFilter(Container& container, FilterFunc func) 
        : mContainer(container) 
        , mFilterFunction(func) 
    {} 
  
    Iterator begin() { return Iterator(*this); } 
    Iterator end() { return Iterator(*this, true); } 
  
private: 
    Container& mContainer; 
    std::function<bool(const value_type& entry)> mFilterFunction; 
};
Le code n'est certainement pas parfait mais vous donne une idée des possibilités. Prenez ceci comme une base.

Voici un exemple d'utilisation, pour afficher les nombres pairs sur [0,9] :
Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
#include <iostream> 
#include <vector> 
int main() 
{ 
    std::vector<int> vec{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 
    for (int v : ForRangedLoopFilter<std::vector<int>>(vec, [](int v) { return v%2 == 0;})) 
    { 
        std::cout << v << std::endl; 
    } 
    return 0; 
}
Pouvoir indiquer le filtre dans la déclaration de la boucle rend le code plus clair sur son but et utilisation selon moi.

Afin d'être utilisé avec un conteneur constant, il suffit d'ajouter un constructeur compatible :
Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <functional> 
template<class Container> 
class ForRangedLoopFilter 
{ 
... 
public: 
... 
    template<class FilterFunc> 
    ForRangedLoopFilter(const Container& container, FilterFunc func) 
        : mContainer(const_cast<Container&>(container)) 
        , mFilterFunction(func) 
    {} 
... 
};
Je garde la référence interne non constante afin de simplifier le code, je ne fais aucune modification du conteneur donc il n'y a pas de soucis.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream> 
#include <string> 
#include <vector> 
int main() 
{ 
    struct Toto { 
        int value; 
        std::string nom; 
    }; 
    const std::vector<Toto> vec{ {0, "toto"}, {1, "tata"}, {2, "titi"} }; 
    for (const Toto& v : ForRangedLoopFilter< std::vector<Toto>>(vec, [](const Toto& v) { return v.value % 2 == 0; })) 
    { 
        std::cout << v.value << ":" << v.nom << std::endl; 
    } 
    return 0; 
}

Une erreur dans cette actualité ? Signalez-le nous !