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?
Vous avez deux choix:
begin
et end
qui peuvent être appelées comme C.begin()
et C.end()
;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)
.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 .
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!=
.
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
.