2012-08-17 20 views

Respuesta

179

Es bastante simple. Digamos que tengo un vector:

std::vector<int> vec; 

Lo termino con algunos datos. Entonces quiero obtener algunos iteradores. Quizás pasarlos. Tal vez a std::for_each:

std::for_each(vec.begin(), vec.end(), SomeFunctor()); 

En C++ 03, SomeFunctor estaba libre para poder modificar el parámetro se pone. Claro, SomeFunctor podría tomar su parámetro por valor o por const&, pero no hay forma de que se asegure de que lo haga. No sin hacer algo tonto como esto:

const std::vector<int> &vec_ref = vec; 
std::for_each(vec_ref.begin(), vec_ref.end(), SomeFunctor()); 

Ahora, introducimos cbegin/cend:

std::for_each(vec.cbegin(), vec.cend(), SomeFunctor()); 

Ahora, tenemos garantías sintácticas que SomeFunctor no pueden modificar los elementos del vector (sin const-yeso, por supuesto). Obtenemos explícitamente const_iterator s, y por lo tanto SomeFunctor :: operator() se llamará con const int &. Si toma sus parámetros como int &, C++ emitirá un error de compilación.


C++ 17 tiene una solución más elegante a este problema: std::as_const. Bueno, al menos es elegante cuando se utiliza for a base de gama:

for(auto &item : std::as_const(vec)) 

Esto simplemente devuelve un const& al objeto que se proporciona.

+1

Pensé que el nuevo protocolo era cbegin (vec) en lugar de vec.cbegin(). –

+18

@Kaz: no hay funciones libres 'std :: cbegin/cend' de la misma manera que' std :: begin/std :: end' existe. Fue un descuido del comité. Si esas funciones existieran, esa sería, en general, la forma de usarlas. –

+15

Aparentemente, 'std :: cbegin/cend' se agregará en C++ 14. Consulte http://en.cppreference.com/w/cpp/iterator/begin –

8

De http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf:

de modo que un programador puede obtener directamente un const_iterator de incluso un recipiente no const

Dieron este ejemplo

vector<MyType> v; 

// fill v ... 
typedef vector<MyType>::iterator iter; 
for(iter it = v.begin(); it != v.end(); ++it) { 
    // use *it ... 
} 

Sin embargo , cuando el recorrido de un contenedor está destinado a inspección solamente, es una práctica generalmente se prefiere usar un const_iterator con el fin para permitir que el compilador para diagnosticar violaciónes const-corrección

Tenga en cuenta que el documento de trabajo también menciona las plantillas del adaptador, que ahora han sido finalizados como std::begin() y std::end() y que también funciona con arreglos nativos. Los correspondientes std::cbegin() y std::cend() están curiosamente ausentes a partir de este momento, pero también pueden ser agregados.

60

Más allá de lo dicho en Nicol Bolas his answer, tenga en cuenta la nueva auto palabra clave:

auto iterator = container.begin(); 

Con auto, no hay manera de asegurarse de que begin() devuelve un operador constante para un contenedor de referencia no constante. Así que ahora lo hace:

auto const_iterator = container.cbegin(); 
+0

¿No podría hacer const auto const_iterator = container.begin()? – allyourcode

+1

@allyourcode: No ayuda. Para el compilador, 'const_iterator' es solo otro identificador. Ninguna versión usa una búsqueda del miembro habitual typedefs 'decltype (contenedor) :: iterator' o' decltype (contenedor) :: const_iterator'. – aschepler

+1

@aschepler No entiendo tu segunda oración, pero creo que te perdiste el "const" delante de "auto" en mi pregunta. Cualquiera que sea el auto, parece que const_iterator debería ser const. – allyourcode

13

tomar esto como un caso de uso práctico

void SomeClass::f(const vector<int>& a) { 
    auto it = someNonConstMemberVector.begin(); 
    ... 
    it = a.begin(); 
    ... 
} 

La asignación falla porque it es un iterador no const. Si utilizó cbegin inicialmente, el iterador tendría el tipo correcto.

3

tropecé con esta pregunta ... Sé que es todavia respondidos y es sólo un nodo lado ...

auto const it = container.begin() es un tipo diferente, entonces auto it = container.cbegin()

la diferencia de int[5] (utilizando el puntero, el cual sé que no tienen el método begin pero muestran muy bien la diferencia ... pero funcionaría en C++ 14 para std::cbegin() y std::cend(), que es esencialmente lo que se debe usar cuando está aquí) ...

int numbers = array[7]; 
const auto it = begin(numbers); // type is int* const -> pointer is const 
auto it = cbegin(numbers);  // type is int const* -> value is const 
1

iterator y const_iterator tienen una relación de herencia y se produce una conversión implícita cuando se compara con o se asigna al otro tipo.

class T {} MyT1, MyT2, MyT3; 
std::vector<T> MyVector = {MyT1, MyT2, MyT3}; 
for (std::vector<T>::const_iterator it=MyVector.begin(); it!=MyVector.end(); ++it) 
{ 
    // ... 
} 

Usando cbegin() y cend() aumentará el rendimiento en este caso.

for (std::vector<T>::const_iterator it=MyVector.cbegin(); it!=MyVector.cend(); ++it) 
{ 
    // ... 
} 
Cuestiones relacionadas