Je me demande pourquoi cbegin
et cend
ont été introduits dans C ++ 11?
Quels sont les cas où l'appel de ces méthodes fait une différence par rapport aux surcharges const de begin
et end
?
Je me demande pourquoi cbegin
et cend
ont été introduits dans C ++ 11?
Quels sont les cas où l'appel de ces méthodes fait une différence par rapport aux surcharges const de begin
et end
?
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'ellesstd::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/beginfor(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 enauto
déduit uneconst
référence. L'OMIauto const& item
est plus simple et plus claire. On ne sait pas pourquoistd::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é:Avec
auto
, il n'y a aucun moyen de s'assurer quebegin()
renvoie un opérateur constant pour une référence de conteneur non constante. Alors maintenant vous faites:la source
const_iterator
c'est juste un autre identifiant. Aucune des deux versions n'utilise une recherche des typedefs de membres habituelsdecltype(container)::iterator
oudecltype(container)::const_iterator
.const_iterator
avecauto
: Ecrivez un modèle de fonction auxiliaire appelémake_const
pour qualifier l'argument objet.Prenez ceci comme un cas d'utilisation pratique
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.la source
De http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf :
Ils ont donné cet exemple
Notez que le document de travail mentionne également les modèles d'adaptateur, qui ont maintenant été finalisés au fur
std::begin()
et à mesurestd::end()
et qui fonctionnent également avec des tableaux natifs. Les correspondantsstd::cbegin()
etstd::cend()
sont curieusement manquants à partir de ce moment, mais ils pourraient également être ajoutés.la source
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 alorsauto 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 pourstd::cbegin()
etstd::cend()
, qui est essentiellement ce que l'on devrait utiliser quand il est ici) ...la source
iterator
etconst_iterator
ont une relation d'héritage et une conversion implicite se produit lors de la comparaison ou de l'affectation à l'autre type.L'utilisation de
cbegin()
etcend()
augmentera les performances dans ce cas.la source
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) enauto
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 lafor
boucle, et comparer à cela, au lieu d'obtenir une nouvelle copie par valeur pour chaque itération. Cela améliorera votre point de vue. : Pconst
peut certainement aider à obtenir de meilleures performances, non pas à cause de la magie duconst
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'ajouterconst
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 :
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:
cela ne fonctionnera pas, car vous ne pouvez pas mettre à jour la valeur en utilisant cbegin et cend qui retourne l'itérateur constant
la source