c++ - examples - use range based for loop instead
c++ 11 sintaxis foreach e iterador personalizado (4)
Como han indicado otros, su contenedor debe implementar las funciones begin()
y end()
(o tener funciones globales o std::
que toman instancias de su contenedor como parámetros).
Esas funciones deben devolver el mismo tipo (generalmente container::iterator
, pero eso es solo una convención). El tipo devuelto debe implementar operator*
, operator++
y operator!=
.
Estoy escribiendo un iterador para un contenedor que se está utilizando en lugar de un contenedor STL. Actualmente, el contenedor STL se usa en muchos lugares con la sintaxis de c ++ 11 foreach, por ejemplo: for(auto &x: C)
. Necesitamos actualizar el código para usar una clase personalizada que envuelva el contenedor 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
};
¿Cómo hago que auto use el iterador correcto para el contenedor personalizado para que el código pueda ser llamado de la siguiente manera ?:
SomeSortedContainer C;
for(auto &x : C){
//do something with x...
}
En general, ¿qué se requiere para garantizar que auto usa el iterador correcto para una clase?
Para poder usar range-based for, su clase debe proporcionar const_iterator begin() const
y const_iterator end() const
members. También puede sobrecargar la función de begin
global, pero tener una función de miembro es mejor en mi opinión. iterator begin()
y const_iterator cbegin() const
también se recomiendan, pero no son obligatorios. Si simplemente desea iterar sobre un único contenedor interno, eso es REALMENTE fácil:
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();}
};
Sin embargo, si desea iterar sobre algo personalizado, probablemente deba diseñar sus propios iteradores como clases dentro de su contenedor.
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
};
Para obtener más detalles sobre cómo escribir una clase de iterador, consulte mi respuesta aquí .
Que yo sepa, SomeSortedContainer
solo necesita proporcionar begin()
y end()
. Y estos deberían devolver un iterador avanzado compatible con el estándar, en su caso SomeSortedContainerIterator
, que en realidad envolvería un std::vector<Type>::iterator
. Con el cumplimiento estándar me refiero a que debe proporcionar los operadores habituales de incremento y desreferenciación, pero también todos aquellos value_type
, value_type
reference_type
, ... tipodef, que a su vez son utilizados por el constructo foreach para determinar el tipo subyacente de los elementos del contenedor. Pero puede enviarlos desde std::vector<Type>::iterator
.
Tienes dos opciones:
- usted proporciona funciones de miembro llamadas
begin
yend
que se pueden llamar comoC.begin()
yC.end()
; - de lo contrario, usted proporciona funciones gratuitas llamadas
begin
yend
que se pueden encontrar utilizando la búsqueda dependiente de argumentos, o en el espacio de nombresstd
, y se puede llamar comobegin(C)
yend(C)
.