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, SomeFunctor
pourrait 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 SomeFunctor
ne peuvent pas modifier les éléments du vecteur (sans const-cast, bien sûr). Nous obtenons explicitement const_iterator
s, 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/cend
fonctions gratuites telles qu'elles std::begin/std::end
existent. C'était une erreur du comité. Si ces fonctions existaient, ce serait généralement la manière de les utiliser.
std::cbegin/cend
sera 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)
?
const
sur la référence. Nicol's considère le conteneur comme const, donc en auto
déduit une const
référence. L'OMI auto const& item
est 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- const
au 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 auto
mot-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_iterator
c'est juste un autre identifiant. Aucune des deux versions n'utilise une recherche des typedefs de membres habituels decltype(container)::iterator
ou decltype(container)::const_iterator
.
const_iterator
avec auto
: Ecrivez un modèle de fonction auxiliaire appelé make_const
pour 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 it
s'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
iterator
et const_iterator
ont 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)
{
// ...
}
const
le 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 auto
fait 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' end
itérateur en déclarant une copie de celui-ci dans la condition d'initialisation de la for
boucle, et comparer à cela, au lieu d'obtenir une nouvelle copie par valeur pour chaque itération. Cela améliorera votre point de vue. : P
const
peut certainement aider à obtenir de meilleures performances, non pas à cause de la magie du const
mot-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.
const
peut (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 const
si 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