Respuestas:
Es bastante simple. Digamos que tengo un vector:
std::vector<int> vec;
Lo lleno con algunos datos. Entonces quiero obtener algunos iteradores. Quizás pasarlos por ahí. Quizás para std::for_each
:
std::for_each(vec.begin(), vec.end(), SomeFunctor());
En C ++ 03, SomeFunctor
era 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 forma de asegurarse de que lo haga. No sin hacer algo tonto como este:
const std::vector<int> &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 que SomeFunctor
no pueden modificar los elementos del vector (sin un const-cast, por supuesto). Obtenemos explícitamente const_iterator
s, y por SomeFunctor::operator()
lo tanto 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 usa basado en rango for
:
for(auto &item : std::as_const(vec))
Esto simplemente devuelve const&
a al objeto que se proporciona.
std::cbegin/cend
funciones libres como std::begin/std::end
existen. Fue un descuido por parte del comité. Si esas funciones existieran, esa sería generalmente la forma de usarlas.
std::cbegin/cend
se agregará en C ++ 14. Ver en.cppreference.com/w/cpp/iterator/begin
for(auto &item : std::as_const(vec))
equivalente a for(const auto &item : vec)
?
const
en la referencia. Nicol ve el contenedor como constante, por lo que auto
deduce una const
referencia. La OMI auto const& item
es más fácil y más clara. No está claro por qué std::as_const()
es bueno aquí; Puedo ver que sería útil cuando se pasa algo que no const
es código genérico donde no podemos controlar el tipo que se usa, pero con rango for
, podemos hacerlo, por lo que me parece un ruido agregado allí.
Más allá de lo que dijo Nicol Bolas en su respuesta , considere la nueva auto
palabra clave:
auto iterator = container.begin();
Con auto
, no hay forma de asegurarse de que begin()
devuelva un operador constante para una referencia de contenedor no constante. Entonces ahora lo haces:
auto const_iterator = container.cbegin();
const_iterator
es solo otro identificador. Ninguna de las versiones utiliza una búsqueda de los miembros habituales typedefs decltype(container)::iterator
o decltype(container)::const_iterator
.
const_iterator
con auto
: Escriba una plantilla de función auxiliar llamada make_const
para calificar el argumento del objeto.
Tome esto como un caso 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 constante. Si usó cbegin inicialmente, el iterador habría tenido el tipo correcto.
De http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf :
para que un programador pueda obtener directamente un const_iterator incluso de un contenedor 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 es solo para inspección, es una práctica generalmente preferida usar un const_iterator para permitir que el compilador diagnostique violaciones de corrección de const
Tenga en cuenta que el documento de trabajo también menciona plantillas de adaptadores, que ahora se han finalizado como std::begin()
y std::end()
y que también funcionan con matrices nativas. Los correspondientes std::cbegin()
y std::cend()
curiosamente faltan a partir de este momento, pero también podrían agregarse.
Me topé con esta pregunta ... Sé que ya está respondida y es solo un nodo lateral ...
auto const it = container.begin()
es un tipo diferente entonces auto it = container.cbegin()
la diferencia para int[5]
(usando el puntero, que sé que no tiene el método de inicio, pero muestra muy 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
iterator
y const_iterator
tienen una relación de herencia y se produce una conversión implícita cuando se compara 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)
{
// ...
}
const
el principal beneficio es el rendimiento (que no lo es: es un código semánticamente correcto y seguro). Pero, si bien tiene un punto, (A) auto
hace que no sea un problema; (B) al hablar sobre el rendimiento, se perdió una cosa principal que debería haber hecho aquí: almacenar en caché el end
iterador declarando una copia de él en la condición init del for
bucle, y compararlo, en lugar de obtener una nueva copia valor para cada iteración. Eso mejorará tu punto. : P
const
definitivamente puede ayudar a lograr un mejor rendimiento, no debido a cierta magia en la const
palabra clave en sí, sino porque el compilador puede habilitar algunas optimizaciones si sabe que los datos no se modificarán, lo que de otra manera no sería posible. Mira este fragmento de la charla de Jason Turner para ver un ejemplo en vivo de esto.
const
puede (casi indirectamente) conducir a beneficios de rendimiento; por si alguien que lee esto puede pensar "No me molestaré en agregar const
si el código generado no se ve afectado de ninguna manera", lo cual no es cierto.
es simple, cbegin devuelve un iterador constante donde begin solo devuelve un iterador
para una mejor comprensión, tomemos dos escenarios aquí
escenario 1 :
#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;
for (int i = 1; i < 6; ++i)
{
/* code */
v.push_back(i);
}
for(auto i = v.begin();i< v.end();i++){
*i = *i + 5;
}
for (auto i = v.begin();i < v.end();i++){
cout<<*i<<" ";
}
return 0;
}
esto se ejecutará porque aquí el iterador i no es constante y puede incrementarse en 5
ahora usemos cbegin y cend denotándolos como escenario de iteradores constantes - 2:
#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;
for (int i = 1; i < 6; ++i)
{
/* code */
v.push_back(i);
}
for(auto i = v.cbegin();i< v.cend();i++){
*i = *i + 5;
}
for (auto i = v.begin();i < v.end();i++){
cout<<*i<<" ";
}
return 0;
}
esto no va a funcionar, porque no puede actualizar el valor usando cbegin y cend, que devuelve el iterador constante