Eu tenho uma classe de contêiner personalizada para a qual gostaria de escrever as classes iterator
e const_iterator
.
Eu nunca fiz isso antes e não consegui encontrar um procedimento apropriado. Quais são as diretrizes sobre a criação do iterador e do que devo estar ciente?
Eu também gostaria de evitar a duplicação de código (sinto isso const_iterator
e iterator
compartilho muitas coisas; uma deve subclassificar a outra?).
Nota de rodapé: Tenho certeza de que o Boost tem algo para facilitar isso, mas não posso usá-lo aqui, por muitas razões estúpidas.
c++
iterator
const-iterator
ereOn
fonte
fonte
Respostas:
std::iterator
comrandom_access_iterator_tag
. Essas classes base definem todas as definições de tipo exigidas pelo STL e fazem outro trabalho.Para evitar a duplicação de código, a classe do iterador deve ser uma classe de modelo e ser parametrizada por "tipo de valor", "tipo de ponteiro", "tipo de referência" ou todos eles (depende da implementação). Por exemplo:
Definições de aviso
iterator_type
econst_iterator_type
tipo: são tipos para seus iteradores não const e const.Consulte também: referência de biblioteca padrão
EDIT:
std::iterator
está obsoleto desde C ++ 17. Veja uma discussão relacionada aqui .fonte
random_access_iterator
não está no padrão e a resposta não lida com a conversão mutable para const. Você provavelmente deseja herdar, por exemplostd::iterator<random_access_iterator_tag, value_type, ... optional arguments ...>
.RefType operator*() { ... }
, estou um passo mais perto - mas não ajuda, porque ainda precisoRefType operator*() const { ... }
.std::iterator
é proposto para descontinuação em C ++ 17 .std::iterator
foi descontinuadoVou mostrar como você pode definir facilmente iteradores para seus contêineres personalizados, mas, no caso de eu ter criado uma biblioteca c ++ 11, você pode criar facilmente iteradores personalizados com comportamento personalizado para qualquer tipo de contêiner, contíguo ou não contíguo.
Você pode encontrá-lo no Github
Aqui estão as etapas simples para criar e usar iteradores personalizados:
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]);};
Por fim, para definir nossas classes de iteradores personalizados:
NOTA: Ao definir iteradores personalizados, derivamos das categorias de iteradores padrão para permitir que os algoritmos STL saibam o tipo de iterador que criamos.
Neste exemplo, defino um iterador de acesso aleatório e um iterador de acesso aleatório reverso:
Agora em algum lugar da sua classe de contêiner personalizado:
fonte
m_data[m_size]
é UB. Você pode simplesmente consertá-lo substituindo-o porm_data+m_size
. Para iteradores reversos, ambosm_data[-1]
em_data-1
estão incorretos (UB). Para consertar reverse_iterators, você precisará usar o "ponteiros para o próximo elemento".Muitas vezes esquecem que isso
iterator
deve ser convertido,const_iterator
mas não o contrário. Aqui está uma maneira de fazer isso:No aviso acima, como se
IntrusiveSlistIterator<T>
converte emIntrusiveSlistIterator<T const>
. SeT
já estiver,const
essa conversão nunca será usada.fonte
const
para nãoconst
.IntrusiveSlistIterator<T const, void>::operator IntrusiveSlistIterator<T const, void>() const
?enable_if
pode consertá-lo, mas ...O Boost tem algo a ajudar: a biblioteca Boost.Iterator.
Mais precisamente nesta página: boost :: iterator_adaptor .
O que é muito interessante é o Exemplo de tutorial, que mostra uma implementação completa, do zero, para um tipo personalizado.
O ponto principal, como já foi citado, é usar uma implementação de modelo único e
typedef
ela.fonte
// a private type avoids misuse
enabler
nunca pretende ser provedor pelo chamador, então meu palpite é que eles o tornam privado para evitar que as pessoas tentem passar por ele acidentalmente. Não acho, de imediato, que isso possa criar qualquer problema para ser realmente aprovado, já que a proteção está dentroenable_if
.Não sei se o Boost tem algo que ajudaria.
Meu padrão preferido é simples: adote um argumento de modelo igual a
value_type
, const qualificado ou não. Se necessário, também um tipo de nó. Então, bem, tudo meio que se encaixa.Lembre-se de parametrizar (template-ize) tudo o que precisa ser, incluindo o construtor copy e
operator==
. Na maioria das vezes, a semântica deconst
criará o comportamento correto.fonte
cur
partir do iterador de constância oposta. A solução que vem à mente éfriend my_container::const_iterator; friend my_container::iterator;
, mas não acho que tenha sido assim antes ... de qualquer maneira, esse esboço geral funciona.friend class
nos dois casos.Existem muitas boas respostas, mas eu criei um cabeçalho de modelo que é bastante conciso e fácil de usar.
Para adicionar um iterador à sua classe, basta escrever uma classe pequena para representar o estado do iterador com 7 pequenas funções, das quais 2 são opcionais:
Em seguida, você pode usá-lo como seria de esperar de um iterador STL:
Espero que ajude.
fonte