J'ai une classe de conteneur personnalisée pour laquelle j'aimerais écrire le iterator
etconst_iterator
classes .
Je n'ai jamais fait cela auparavant et je n'ai pas réussi à trouver un mode d'emploi approprié. Quelles sont les directives concernant la création d'itérateurs et que dois-je savoir?
Je voudrais également éviter la duplication de code (je le ressens const_iterator
et iterator
partage beaucoup de choses; doit-on sous-classer l'autre?).
Note de bas de page: je suis presque sûr que Boost a quelque chose pour faciliter cela, mais je ne peux pas l'utiliser ici, pour de nombreuses raisons stupides.
c++
iterator
const-iterator
ereOn
la source
la source
Réponses:
std::iterator
avecrandom_access_iterator_tag
. Ces classes de base définissent toutes les définitions de type requises par STL et effectuent d'autres travaux.Pour éviter la duplication de code, la classe d'itérateur doit être une classe de modèle et être paramétrée par "type de valeur", "type de pointeur", "type de référence" ou toutes (dépend de l'implémentation). Par exemple:
Remarquez
iterator_type
etconst_iterator_type
définissez les types: ce sont des types pour vos itérateurs non const et const.Voir aussi: référence de bibliothèque standard
EDIT:
std::iterator
est obsolète depuis C ++ 17. Voir une discussion connexe ici .la source
random_access_iterator
n'est pas dans la norme et la réponse ne gère pas la conversion mutable en const. Vous voulez probablement hériter de, par exemplestd::iterator<random_access_iterator_tag, value_type, ... optional arguments ...>
.RefType operator*() { ... }
, je suis un pas de plus - mais cela n'aide pas, car j'ai encore besoinRefType operator*() const { ... }
.std::iterator
est proposé pour dépréciation en C ++ 17 .std::iterator
est obsolèteJe vais vous montrer comment vous pouvez facilement définir des itérateurs pour vos conteneurs personnalisés, mais juste au cas où j'ai créé une bibliothèque c ++ 11 qui vous permet de créer facilement des itérateurs personnalisés avec un comportement personnalisé pour tout type de conteneur, contigu ou non contiguë.
Vous pouvez le trouver sur Github
Voici les étapes simples pour créer et utiliser des itérateurs personnalisés:
typedef blRawIterator< Type > iterator;
typedef blRawIterator< const Type > const_iterator;
iterator begin(){return iterator(&m_data[0]);};
const_iterator cbegin()const{return const_iterator(&m_data[0]);};
Enfin, pour définir nos classes d'itérateurs personnalisées:
REMARQUE: lors de la définition d'itérateurs personnalisés, nous dérivons des catégories d'itérateurs standard pour indiquer aux algorithmes STL le type d'itérateur que nous avons créé.
Dans cet exemple, je définis un itérateur d'accès aléatoire et un itérateur d'accès aléatoire inverse:
Maintenant quelque part dans votre classe de conteneur personnalisé:
la source
m_data[m_size]
est UB. Vous pouvez simplement le réparer en le remplaçant parm_data+m_size
. Pour les itérateurs inversés, les deuxm_data[-1]
etm_data-1
sont incorrects (UB). Pour corriger les reverse_iterators, vous devrez utiliser les "pointeurs vers le prochain élément".Ils oublient souvent que cela
iterator
doit se convertirconst_iterator
mais pas l'inverse. Voici une façon de procéder:Dans la remarque ci-dessus, comment se
IntrusiveSlistIterator<T>
convertitIntrusiveSlistIterator<T const>
. SiT
est déjàconst
cette conversion n'est jamais utilisée.la source
const
nonconst
.IntrusiveSlistIterator<T const, void>::operator IntrusiveSlistIterator<T const, void>() const
?enable_if
problème pourrait êtreBoost a quelque chose à vous aider: la bibliothèque Boost.Iterator.
Plus précisément cette page: boost :: iterator_adaptor .
Ce qui est très intéressant, c'est l' exemple de didacticiel qui montre une implémentation complète, à partir de zéro, pour un type personnalisé.
Le point principal, comme cela a déjà été mentionné, est d'utiliser une seule implémentation de modèle et
typedef
cela.la source
// a private type avoids misuse
enabler
n'est jamais destiné à être fournisseur par l'appelant, donc je suppose qu'ils le rendent privé pour éviter que des personnes tentent accidentellement de le transmettre. Je ne pense pas, à première vue, que cela pourrait créer un problème pour le passer, car la protection résideenable_if
.Je ne sais pas si Boost a quelque chose qui pourrait aider.
Mon modèle préféré est simple: prenez un argument de modèle qui est égal à
value_type
, soit const qualifié ou non. Si nécessaire, également un type de nœud. Ensuite, eh bien, tout se met en place.N'oubliez pas de paramétrer (template-ize) tout ce qui doit être, y compris le constructeur de copie et
operator==
. Pour la plupart, la sémantique deconst
créera un comportement correct.la source
cur
partir de l'itérateur de constance opposée. La solution qui me vient à l'esprit estfriend my_container::const_iterator; friend my_container::iterator;
, mais je ne pense pas que ce soit comme ça que je l'ai fait avant… de toute façon ce schéma général fonctionne.friend class
dans les deux cas.Il y a plein de bonnes réponses mais j'ai créé un en- tête de modèle que j'utilise qui est assez concis et facile à utiliser.
Pour ajouter un itérateur à votre classe, il suffit d'écrire une petite classe pour représenter l'état de l'itérateur avec 7 petites fonctions, dont 2 facultatives:
Ensuite, vous pouvez l'utiliser comme vous vous attendez d'un itérateur STL:
J'espère que ça aide.
la source