web-dev-qa-db-fra.com

Quelle est la raison derrière cbegin / cend?

Je me demande pourquoi cbegin et cend ont été introduits en C++ 11?

Dans quels cas appeler ces méthodes fait-il une différence par rapport aux surcharges const de begin et end?

179
Andrey

C'est assez simple. Dis que j'ai un vecteur:

std::vector<int> vec;

Je le remplis avec des données. Ensuite, je veux obtenir quelques itérateurs. Peut-être les faire circuler. Peut-être à std::for_each:

std::for_each(vec.begin(), vec.end(), SomeFunctor());

En C++ 03, SomeFunctor était libre de pouvoir modifier le paramètre obtenu. Bien sûr, SomeFunctor pourrait prendre son paramètre par valeur ou par const&, Mais il n’ya aucun moyen d’en assurer qu’il le fasse. . Non sans faire une bêtise pareille:

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 que SomeFunctor ne peut pas modifier les éléments du vecteur (sans const-cast, bien sûr). Nous obtenons explicitement const_iterator S, et donc SomeFunctor::operator() sera appelé avec const int &. Si cela 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 . Au moins, c’est élégant lorsque vous utilisez for:

for(auto &item : std::as_const(vec))

Ceci renvoie simplement un const& À l'objet pour lequel il est fourni.

214
Nicol Bolas

Au-delà de ce que Nicol Bolas a dit dans sa réponse , considérons le nouveau mot-clé auto:

auto iterator = container.begin();

Avec auto, il n’ya aucun moyen de s’assurer que begin() renvoie un opérateur constant pour une référence de conteneur non constante. Alors maintenant tu fais:

auto const_iterator = container.cbegin();
63
Stefan Majewsky

Prends ça comme un cas pratique

void SomeClass::f(const vector<int>& a) {
  auto it = someNonConstMemberVector.begin();
  ...
  it = a.begin();
  ...
}

L'attribution échoue parce que it est un itérateur non constant. Si vous utilisiez initialement cbegin, l'itérateur aurait eu le bon type.

14

De http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf :

de sorte qu'un programmeur puisse obtenir directement un const_iterator 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 uniquement destinée à une inspection, il est généralement préférable d'utiliser un const_iterator afin de permettre au compilateur de diagnostiquer des violations de correction de la conformité const.

Notez que le document de travail mentionne également les modèles d'adaptateur, qui ont maintenant été finalisés sous la forme std::begin() et std::end(), et qui fonctionnent également avec des tableaux natifs. Les std::cbegin() et std::cend() correspondantes manquent curieusement à ce moment-là, mais ils pourraient également être ajoutés.

8
TemplateRex

Je suis tombé par hasard sur cette question ... Je sais que c'est déjà répondu et que c'est juste un noeud de côté ...

auto const it = container.begin() est d'un type différent de auto it = container.cbegin()

la différence pour int[5] (en utilisant un pointeur, qui, je le 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 qu'il faut utiliser quand c'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
5
chris g.

iterator et const_iterator ont une relation d'héritage et une conversion implicite se produit lorsque comparé à ou affecté à 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)
{
    // ...
}
2
hkBattousai