¿Cuál es la razón detrás de cbegin / cend?

Me pregunto por qué cbegin y cend se introdujeron en C ++ 11?

¿Cuáles son los casos cuando llamar a estos métodos hace una diferencia con las sobrecargas de begin y end ?

Es bastante simple. Digamos que tengo un vector:

 std::vector vec; 

Lo llené con algunos datos. Entonces quiero obtener algunos iteradores. Quizás pasarlos. Tal vez para std::for_each :

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

En C ++ 03, SomeFunctor fue libre de poder modificar el parámetro que obtiene. Claro, SomeFunctor podría tomar su parámetro por valor o por const& , pero no hay manera de asegurarse de que lo haga. No sin hacer algo tonto como este:

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

Ahora, presentamos cbegin/cend :

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

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


C ++ 17 tiene una solución más elegante para este problema: std::as_const . Bueno, al menos es elegante cuando se usa el rango for :

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

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

Más allá de lo que dijo Nicol Bolas en su respuesta , considere la nueva palabra clave auto :

 auto iterator = container.begin(); 

Con auto , no hay forma de asegurarse de que begin() devuelve un operador constante para una referencia de contenedor no constante. Entonces ahora lo haces:

 auto const_iterator = container.cbegin(); 

Toma esto como un uso práctico

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

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

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

para que un progtwigdor pueda obtener directamente un const_iterator incluso desde un contenedor no const

Ellos dieron este ejemplo

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

Sin embargo, cuando un cruce de contenedores está destinado a inspección solamente, es una práctica generalmente preferida usar un const_iterator para permitir que el comstackdor diagnostique las violaciones de const-correctness

Tenga en cuenta que el documento de trabajo también menciona las plantillas de adaptador, que ahora se han finalizado como std::begin() y std::end() y que también funcionan con matrices nativas. El std::cbegin() y std::cend() correspondientes están curiosamente ausentes a partir de este momento, pero también pueden ser agregados.

Acabo de tropezar con esta pregunta … Sé que ya está respondida y es solo un nodo lateral …

auto const it = container.begin() es un tipo diferente y luego auto it = container.cbegin()

la diferencia para int[5] (usando el puntero, que sé que no tiene el método begin pero muestra bien la diferencia … pero funcionaría en c ++ 14 para std::cbegin() y std::cend() , que es esencialmente lo que uno debería 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 

const_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 MyVector = {MyT1, MyT2, MyT3}; for (std::vector::const_iterator it=MyVector.begin(); it!=MyVector.end(); ++it) { // ... } 

Usar cbegin() y cend() boostá el rendimiento en este caso.

 for (std::vector::const_iterator it=MyVector.cbegin(); it!=MyVector.cend(); ++it) { // ... }