Me pregunto por qué cbegin
y cend
se introdujeron en C ++ 11?
¿Cuáles son los casos en los que llamar a estos métodos marca la diferencia de las sobrecargas constantes de begin
y end
?
Me pregunto por qué cbegin
y cend
se introdujeron en C ++ 11?
¿Cuáles son los casos en los que llamar a estos métodos marca la diferencia de las sobrecargas constantes de begin
y end
?
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 comostd::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/beginfor(auto &item : std::as_const(vec))
equivalente afor(const auto &item : vec)
?const
en la referencia. Nicol ve el contenedor como constante, por lo queauto
deduce unaconst
referencia. La OMIauto 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 noconst
es código genérico donde no podemos controlar el tipo que se usa, pero con rangofor
, 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:Con
auto
, no hay forma de asegurarse de quebegin()
devuelva un operador constante para una referencia de contenedor no constante. Entonces ahora lo haces:fuente
const_iterator
es solo otro identificador. Ninguna de las versiones utiliza una búsqueda de los miembros habituales typedefsdecltype(container)::iterator
odecltype(container)::const_iterator
.const_iterator
conauto
: Escriba una plantilla de función auxiliar llamadamake_const
para calificar el argumento del objeto.Tome esto como un caso práctico
La asignación falla porque
it
es un iterador no constante. Si usó cbegin inicialmente, el iterador habría tenido el tipo correcto.fuente
De http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf :
Dieron este ejemplo
Tenga en cuenta que el documento de trabajo también menciona plantillas de adaptadores, que ahora se han finalizado como
std::begin()
ystd::end()
y que también funcionan con matrices nativas. Los correspondientesstd::cbegin()
ystd::cend()
curiosamente faltan a partir de este momento, pero también podrían agregarse.fuente
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 entoncesauto 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 parastd::cbegin()
ystd::cend()
, que es esencialmente lo que uno debería usar cuando está aquí) ...fuente
iterator
yconst_iterator
tienen una relación de herencia y se produce una conversión implícita cuando se compara o se asigna al otro tipo.Usando
cbegin()
ycend()
aumentará el rendimiento en este caso.fuente
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é elend
iterador declarando una copia de él en la condición init delfor
bucle, y compararlo, en lugar de obtener una nueva copia valor para cada iteración. Eso mejorará tu punto. : Pconst
definitivamente puede ayudar a lograr un mejor rendimiento, no debido a cierta magia en laconst
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 agregarconst
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 :
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:
esto no va a funcionar, porque no puede actualizar el valor usando cbegin y cend, que devuelve el iterador constante
fuente