Risposte:
È abbastanza semplice. Di 'che ho un vettore:
std::vector<int> vec;
Lo riempio con alcuni dati. Quindi voglio ottenere alcuni iteratori. Forse passarli in giro. Forse per std::for_each
:
std::for_each(vec.begin(), vec.end(), SomeFunctor());
In C ++ 03, SomeFunctor
era libero di poter modificare il parametro che ottiene. Certo, SomeFunctor
potrebbe prendere il suo parametro per valore o per const&
, ma non c'è modo di assicurarselo . Non senza fare qualcosa di stupido come questo:
const std::vector<int> &vec_ref = vec;
std::for_each(vec_ref.begin(), vec_ref.end(), SomeFunctor());
Ora presentiamo cbegin/cend
:
std::for_each(vec.cbegin(), vec.cend(), SomeFunctor());
Ora, abbiamo assicurazioni sintattiche che SomeFunctor
non possono modificare gli elementi del vettore (senza un const-cast, ovviamente). Riceviamo esplicitamente const_iterator
s, e quindi SomeFunctor::operator()
verremo chiamati con const int &
. Se accetta i parametri come int &
, C ++ genererà un errore del compilatore.
C ++ 17 ha una soluzione più elegante a questo problema: std::as_const
. Bene, almeno è elegante quando si usa il range-based for
:
for(auto &item : std::as_const(vec))
Ciò restituisce semplicemente const&
a l'oggetto che viene fornito.
std::cbegin/cend
funzioni libere nel modo in cui std::begin/std::end
esistono. È stata una svista da parte del comitato. Se tali funzioni esistessero, quello sarebbe generalmente il modo di usarle.
std::cbegin/cend
verrà aggiunto in C ++ 14. Vedi en.cppreference.com/w/cpp/iterator/begin
for(auto &item : std::as_const(vec))
equivalente a for(const auto &item : vec)
?
const
il riferimento. Nicol considera il contenitore come const, quindi auto
deduce un const
riferimento. L'IMO auto const& item
è più semplice e chiaro. Non è chiaro perché std::as_const()
qui va bene; Vedo che sarebbe utile quando si passa qualcosa di non const
generico a codice in cui non possiamo controllare il tipo che viene utilizzato, ma con range- for
, possiamo, quindi mi sembra solo un rumore aggiunto lì.
Oltre a ciò che ha detto Nicol Bolas nella sua risposta , considera la nuova auto
parola chiave:
auto iterator = container.begin();
Con auto
, non c'è modo di assicurarsi che begin()
restituisca un operatore costante per un riferimento contenitore non costante. Quindi ora fai:
auto const_iterator = container.cbegin();
const_iterator
è solo un altro identificatore. Nessuna versione utilizza una ricerca dei soliti typedef dei membri decltype(container)::iterator
o decltype(container)::const_iterator
.
const_iterator
con auto
: Scrivi un modello di funzione ausiliaria chiamato make_const
per qualificare l'argomento oggetto.
Prendi questo come un pratico caso d'uso
void SomeClass::f(const vector<int>& a) {
auto it = someNonConstMemberVector.begin();
...
it = a.begin();
...
}
L'assegnazione non riesce perché it
è un iteratore non costante. Se inizialmente avessi usato cbegin, l'iteratore avrebbe avuto il tipo giusto.
Da http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf :
in modo che un programmatore possa ottenere direttamente un const_iterator anche da un contenitore non const
Hanno dato questo esempio
vector<MyType> v;
// fill v ...
typedef vector<MyType>::iterator iter;
for( iter it = v.begin(); it != v.end(); ++it ) {
// use *it ...
}
Tuttavia, quando un attraversamento di container è destinato solo a ispezione, è generalmente preferibile utilizzare un const_iterator per consentire al compilatore di diagnosticare le violazioni della correttezza della const
Si noti che il documento di lavoro menziona anche adattatori modelli, che ora sono stati finalizzati, come std::begin()
e std::end()
, e che anche il lavoro con gli array native. I corrispondenti std::cbegin()
e std::cend()
sono curiosamente mancanti al momento, ma potrebbero anche essere aggiunti.
Mi sono appena imbattuto in questa domanda ... So che è Alredy answerd ed è solo un nodo laterale ...
auto const it = container.begin()
è un tipo diverso quindi auto it = container.cbegin()
la differenza per int[5]
(usando il puntatore, che conosco non ha il metodo iniziale ma mostra bene la differenza ... ma funzionerebbe in c ++ 14 per std::cbegin()
e std::cend()
, che è essenzialmente quello che si dovrebbe usare quando è qui) ...
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
e const_iterator
hanno una relazione di ereditarietà e si verifica una conversione implicita se confrontata con o assegnata all'altro 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)
{
// ...
}
Utilizzando cbegin()
e cend()
aumenterà le prestazioni in questo caso.
for (std::vector<T>::const_iterator it=MyVector.cbegin(); it!=MyVector.cend(); ++it)
{
// ...
}
const
il principale vantaggio è la prestazione (cosa che non è: è un codice semanticamente corretto e sicuro). Ma, mentre hai un punto, (A) lo auto
rende un problema; (B) parlando delle prestazioni, ti sei perso una cosa principale che avresti dovuto fare qui: memorizza nella cache l' end
iteratore dichiarandone una copia nella condizione init del for
ciclo, e confrontalo con quello, invece di ottenere una nuova copia da valore per ogni iterazione. Ciò renderà il tuo punto migliore. : P
const
può sicuramente aiutare a ottenere prestazioni migliori, non a causa della magia nella const
parola chiave stessa, ma perché il compilatore può abilitare alcune ottimizzazioni se sa che i dati non verranno modificati, altrimenti non sarebbe possibile. Dai un'occhiata a questo pezzo del discorso di Jason Turner per un esempio dal vivo di questo.
const
può (quasi indirettamente) portare a benefici in termini di prestazioni; nel caso qualcuno che legga questo potrebbe pensare "Non mi preoccuperò di aggiungere const
se il codice generato non è influenzato in alcun modo", il che non è vero.
è semplice, cbegin restituisce un iteratore costante in cui inizio restituisce solo un iteratore
per una migliore comprensione, prendiamo due scenari qui
scenario 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;
}
questo funzionerà perché qui iteratore i non è costante e può essere incrementato di 5
ora usiamo cbegin e cend denotandoli come scenario di iteratori costanti - 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;
}
questo non funzionerà, perché non puoi aggiornare il valore usando cbegin e cend che restituisce l'iteratore costante