Me pregunto por qué cbegin
y cend
se introdujeron en C++ 11?¿Cuál es la razón detrás de cbegin/cend?
¿Cuáles son los casos cuando llamar a estos métodos hace una diferencia con las sobrecargas const de begin
y end
?
Me pregunto por qué cbegin
y cend
se introdujeron en C++ 11?¿Cuál es la razón detrás de cbegin/cend?
¿Cuáles son los casos cuando llamar a estos métodos hace una diferencia con las sobrecargas const de begin
y end
?
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.
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.
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();
¿No podría hacer const auto const_iterator = container.begin()? – allyourcode
@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
@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
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.
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
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)
{
// ...
}
Pensé que el nuevo protocolo era cbegin (vec) en lugar de vec.cbegin(). –
@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. –
Aparentemente, 'std :: cbegin/cend' se agregará en C++ 14. Consulte http://en.cppreference.com/w/cpp/iterator/begin –