web-dev-qa-db-fra.com

c ++ 11 foreach syntaxe et itérateur personnalisé

J'écris un itérateur pour un conteneur qui est utilisé à la place d'un conteneur STL. Actuellement, le conteneur STL est utilisé à de nombreux endroits avec la c ++ 11 foreach syntax par exemple: for(auto &x: C). Nous avons dû mettre à jour le code pour utiliser une classe personnalisée qui encapsule le conteneur STL:

template< typename Type>
class SomeSortedContainer{
    std::vector<typename Type> m_data; //we wish to iterate over this
    //container implementation code
};    
class SomeSortedContainerIterator{
    //iterator code
};

Comment puis-je obtenir automatique pour utiliser l'itérateur correct pour le conteneur personnalisé afin que le code puisse être appelé de la manière suivante?:

SomeSortedContainer C;
for(auto &x : C){
    //do something with x... 
}

En général, que faut-il pour que auto utilise l'itérateur correct pour une classe?

59
shuttle87

Vous avez deux choix:

  • vous fournissez des fonctions membres nommées begin et end qui peuvent être appelées comme C.begin() et C.end();
  • sinon, vous fournissez des fonctions gratuites nommées begin et end qui peuvent être trouvées à l'aide de la recherche dépendante de l'argument, ou dans l'espace de noms std, et peuvent être appelées comme begin(C) et end(C).
51

Pour pouvoir utiliser une plage basée sur, votre classe doit fournir des membres const_iterator begin() const et const_iterator end() const. Vous pouvez également surcharger la fonction globale begin, mais avoir une fonction membre est mieux à mon avis. iterator begin() et const_iterator cbegin() const sont également recommandés, mais pas obligatoires. Si vous voulez simplement parcourir un seul conteneur interne, c'est VRAIMENT facile:

template< typename Type>
class SomeSortedContainer{

    std::vector<Type> m_data; //we wish to iterate over this
    //container implementation code
public:
    typedef typename std::vector<Type>::iterator iterator;
    typedef typename std::vector<Type>::const_iterator const_iterator;

    iterator begin() {return m_data.begin();}
    const_iterator begin() const {return m_data.begin();}
    const_iterator cbegin() const {return m_data.cbegin();}
    iterator end() {return m_data.end();}
    const_iterator end() const {return m_data.end();}
    const_iterator cend() const {return m_data.cend();}
};    

Si vous souhaitez itérer sur quelque chose de personnalisé, vous devrez probablement concevoir vos propres itérateurs en tant que classes à l'intérieur de votre conteneur.

class const_iterator : public std::iterator<random_access_iterator_tag, Type>{
    typename std::vector<Type>::iterator m_data;
    const_iterator(typename std::vector<Type>::iterator data) :m_data(data) {}
public:
    const_iterator() :m_data() {}
    const_iterator(const const_iterator& rhs) :m_data(rhs.m_data) {}
     //const iterator implementation code
};

Pour plus de détails sur l'écriture d'une classe itérateur, voir ma réponse ici .

51
Mooing Duck

Comme d'autres l'ont indiqué, votre conteneur doit implémenter les fonctions begin() et end() (ou avoir des fonctions globales ou std:: fonctions qui prennent des instances de votre conteneur comme paramètres).

Ces fonctions doivent renvoyer le même type (généralement container::iterator, mais ce n'est qu'une convention). Le type retourné doit implémenter operator*, operator++, et operator!=.

7
dspeyer

A ma connaissance, SomeSortedContainer a juste besoin de fournir begin() et end(). Et ceux-ci devraient renvoyer un itérateur vers l'avant conforme à la norme, dans votre cas SomeSortedContainerIterator, qui ferait en fait un std::vector<Type>::iterator. Avec le standard, je veux dire qu'il doit fournir les opérateurs habituels d'incrémentation et de déréférencement, mais aussi tous ceux value_type, reference_type, ... typedefs, qui à leur tour sont utilisés par la construction foreach pour déterminer le type sous-jacent des éléments de conteneur. Mais vous pourriez simplement les transmettre à partir du std::vector<Type>::iterator.

2
Christian Rau