c++ - cplusplus - vector cppreference
¿Cuál es la razón detrás de cbegin/cend? (6)
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
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
?
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 desde un contenedor no const
Ellos 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 un cruce de contenedores está destinado a inspección solamente, es una práctica generalmente preferida usar un const_iterator para permitir que el compilador 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.
Es bastante simple. Digamos que tengo un vector:
std::vector<int> 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<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 de que SomeFunctor
no puede modificar los elementos del vector (sin un molde constante, 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 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<int>& 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.
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)
{
// ...
}
Usar cbegin()
y cend()
aumentará el rendimiento en este caso.
for (std::vector<T>::const_iterator it=MyVector.cbegin(); it!=MyVector.cend(); ++it)
{
// ...
}