Réponses:
C'est assez simple. Disons que j'ai un vecteur:
std::vector<int> vec;
Je le remplis de quelques données. Ensuite, je veux y trouver des itérateurs. Peut-être les faire circuler. Peut-être pour std::for_each:
std::for_each(vec.begin(), vec.end(), SomeFunctor());
En C ++ 03, SomeFunctorétait libre de pouvoir modifier le paramètre qu'il obtient. Bien sûr, SomeFunctorpourrait prendre son paramètre par valeur ou par const&, mais il n'y a aucun moyen de garantir que c'est le cas. Non sans faire quelque chose de stupide comme ça:
const std::vector<int> &vec_ref = vec;
std::for_each(vec_ref.begin(), vec_ref.end(), SomeFunctor());
Maintenant, nous introduisons cbegin/cend:
std::for_each(vec.cbegin(), vec.cend(), SomeFunctor());
Maintenant, nous avons des assurances syntaxiques qui SomeFunctorne peuvent pas modifier les éléments du vecteur (sans const-cast, bien sûr). Nous obtenons explicitement const_iterators, et nous SomeFunctor::operator()serons donc appelés avec const int &. S'il prend ses paramètres comme int &, C ++ émettra une erreur de compilation.
C ++ 17 a une solution plus élégante à ce problème: std::as_const. Eh bien, au moins, c'est élégant lors de l'utilisation de la plage for:
for(auto &item : std::as_const(vec))
Cela renvoie simplement un const&à l'objet qui lui est fourni.
std::cbegin/cendfonctions gratuites telles qu'elles std::begin/std::endexistent. C'était une erreur du comité. Si ces fonctions existaient, ce serait généralement la manière de les utiliser.
std::cbegin/cendsera ajouté en C ++ 14. Voir en.cppreference.com/w/cpp/iterator/begin
for(auto &item : std::as_const(vec))équivaut à for(const auto &item : vec)?
constsur la référence. Nicol's considère le conteneur comme const, donc en autodéduit une constréférence. L'OMI auto const& itemest plus simple et plus claire. On ne sait pas pourquoi std::as_const()c'est bon ici; Je peux voir que ce serait utile lors du passage de quelque chose de non- constau code générique où nous ne pouvons pas contrôler le type qui est utilisé, mais avec range- for, nous pouvons, donc cela me semble juste comme du bruit supplémentaire.
Au-delà de ce que Nicol Bolas a dit dans sa réponse , considérons le nouveau automot-clé:
auto iterator = container.begin();
Avec auto, il n'y a aucun moyen de s'assurer que begin()renvoie un opérateur constant pour une référence de conteneur non constante. Alors maintenant vous faites:
auto const_iterator = container.cbegin();
const_iteratorc'est juste un autre identifiant. Aucune des deux versions n'utilise une recherche des typedefs de membres habituels decltype(container)::iteratorou decltype(container)::const_iterator.
const_iteratoravec auto: Ecrivez un modèle de fonction auxiliaire appelé make_constpour qualifier l'argument objet.
Prenez ceci comme un cas d'utilisation pratique
void SomeClass::f(const vector<int>& a) {
auto it = someNonConstMemberVector.begin();
...
it = a.begin();
...
}
L'affectation échoue car il its'agit d'un itérateur non constant. Si vous avez utilisé cbegin au départ, l'itérateur aurait eu le bon type.
De http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf :
afin qu'un programmeur puisse directement obtenir un const_iterator à partir même d'un conteneur non-const
Ils ont donné cet exemple
vector<MyType> v;
// fill v ...
typedef vector<MyType>::iterator iter;
for( iter it = v.begin(); it != v.end(); ++it ) {
// use *it ...
}
Cependant, lorsqu'une traversée de conteneur est destinée à l'inspection uniquement, il est généralement préférable d'utiliser un const_iterator afin de permettre au compilateur de diagnostiquer les violations de const-correctness
Notez que le document de travail mentionne également les modèles d'adaptateur, qui ont maintenant été finalisés au fur std::begin()et à mesure std::end()et qui fonctionnent également avec des tableaux natifs. Les correspondants std::cbegin()et std::cend()sont curieusement manquants à partir de ce moment, mais ils pourraient également être ajoutés.
Je suis juste tombé sur cette question ... Je sais que c'est déjà répondu et c'est juste un nœud secondaire ...
auto const it = container.begin() est un type différent alors auto it = container.cbegin()
la différence pour int[5](en utilisant un pointeur, qui, je sais, n'a pas la méthode begin mais montre bien la différence ... mais fonctionnerait en c ++ 14 pour std::cbegin()et std::cend(), qui est essentiellement ce que l'on devrait utiliser quand il est ici) ...
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
iteratoret const_iteratoront une relation d'héritage et une conversion implicite se produit lors de la comparaison ou de l'affectation à l'autre type.
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)
{
// ...
}
L'utilisation de cbegin()et cend()augmentera les performances dans ce cas.
for (std::vector<T>::const_iterator it=MyVector.cbegin(); it!=MyVector.cend(); ++it)
{
// ...
}
constle principal avantage est la performance (ce qui n'est pas le cas: c'est un code sémantiquement correct et sûr). Mais, pendant que vous avez un point, (A) en autofait un non-problème; (B) en parlant de performances, vous avez manqué une chose principale que vous auriez dû faire ici: mettre en cache l' enditérateur en déclarant une copie de celui-ci dans la condition d'initialisation de la forboucle, et comparer à cela, au lieu d'obtenir une nouvelle copie par valeur pour chaque itération. Cela améliorera votre point de vue. : P
constpeut certainement aider à obtenir de meilleures performances, non pas à cause de la magie du constmot-clé lui-même, mais parce que le compilateur peut activer certaines optimisations s'il sait que les données ne seront pas modifiées, ce qui ne serait pas possible autrement. Regardez ce extrait d'une conférence de Jason Turner pour un exemple en direct de cela.
constpeut (presque indirectement) conduire à des avantages de performance; juste au cas où quelqu'un lisant ceci pourrait penser "Je ne prendrai pas la peine d'ajouter constsi le code généré n'est jamais affecté de quelque manière que ce soit", ce qui n'est pas vrai.
c'est simple, cbegin renvoie un itérateur constant où begin renvoie juste un itérateur
pour une meilleure compréhension, prenons deux scénarios ici
Scénario 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;
}
cela fonctionnera car ici l'itérateur i n'est pas constant et peut être incrémenté de 5
maintenant, utilisons cbegin et cend en les désignant comme scénario d'itérateurs constants - 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;
}
cela ne fonctionnera pas, car vous ne pouvez pas mettre à jour la valeur en utilisant cbegin et cend qui retourne l'itérateur constant