Je me demande pourquoi cbegin
y cend
ont été introduites dans C++11 ?
Quels sont les cas où l'appel de ces méthodes fait une différence par rapport aux surcharges constantes de begin
y end
?
Je me demande pourquoi cbegin
y cend
ont été introduites dans C++11 ?
Quels sont les cas où l'appel de ces méthodes fait une différence par rapport aux surcharges constantes de begin
y end
?
C'est assez simple. Disons que j'ai un vecteur :
std::vector<int> vec;
Je le remplis avec des données. Ensuite, je veux lui donner des itérateurs. Peut-être les passer autour. 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 qu'il le fait. Pas 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 l'assurance syntaxique que SomeFunctor
ne peut pas modifier les éléments du vecteur (sans un const-cast, bien sûr). On obtient explicitement const_iterator
et donc SomeFunctor::operator()
sera appelé avec const int &
. S'il prend ses paramètres comme int &
le C++ émettra une erreur de compilation.
Le C++17 offre une solution plus élégante à ce problème : std::as_const
. Au moins, c'est élégant lorsque l'on utilise la méthode de l'intervalle. for
:
for(auto &item : std::as_const(vec))
Cela renvoie simplement un const&
à l'objet qui lui est fourni.
Au-delà de ce que Nicol Bolas a dit dans sa réponse considère la nouvelle auto
mot-clé :
auto iterator = container.begin();
Avec auto
il n'y a aucun moyen d'être sûr que begin()
renvoie un opérateur constant pour une référence de conteneur non constante. Donc, maintenant vous le faites :
auto const_iterator = container.cbegin();
@allyourcode : Ça n'aide pas. Au compilateur, const_iterator
est juste un autre identifiant. Aucune des deux versions n'utilise la consultation des typedefs membres habituels decltype(container)::iterator
o decltype(container)::const_iterator
.
@aschepler Je ne comprends pas votre deuxième phrase, mais je pense que vous avez manqué le "const" devant "auto" dans ma question. Quel que soit le résultat de auto, il semble que const_iterator devrait être const.
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 parce que it
est un itérateur non-constant. Si vous aviez utilisé cbegin initialement, l'itérateur aurait eu le bon type.
Desde http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf :
afin qu'un programmeur puisse obtenir directement un const_iterator, même à partir d'un conteneur non constant. 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 uniquement à l'inspection, il est généralement préférable d'utiliser un const_iterator afin de permettre au pour permettre au compilateur de diagnostiquer les violations de const-correctness.
Notez que le document de travail mentionne également des modèles d'adaptateurs, qui ont maintenant été finalisés en tant que std::begin()
y std::end()
et qui fonctionnent également avec les tableaux natifs. Les tableaux correspondants std::cbegin()
y std::cend()
sont curieusement absents à ce jour, mais ils pourraient également être ajoutés.
Je viens de tomber sur cette question... Je sais qu'il y a déjà une réponse et que c'est juste un noeud latéral...
auto const it = container.begin()
est un type différent de celui de auto it = container.cbegin()
la différence pour int[5]
(en utilisant le pointeur, qui je sais n'a pas la méthode begin mais qui montre bien la différence... mais qui fonctionnerait en c++14 pour std::cbegin()
y std::cend()
qui est essentiellement ce que l'on devrait utiliser quand c'est là)...
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
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.