205 votes

Quelle est la raison de cbegin/cend ?

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 ?

241voto

Nicol Bolas Points 133791

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.

68voto

Stefan Majewsky Points 2578

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();

1 votes

Ne pourriez-vous pas faire const auto const_iterator = container.begin() ?

2 votes

@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 .

2 votes

@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.

15voto

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.

8voto

TemplateRex Points 26447

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.

5voto

chris g. Points 68

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.com

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.

Powered by:

X