it-swarm.com.de

Was ist der Grund für cbegin / cend?

Ich frage mich, warum cbegin und cend in C++ 11 eingeführt wurden.

In welchen Fällen unterscheidet sich der Aufruf dieser Methoden von const-Überladungen von begin und end?

179
Andrey

Es ist ganz einfach. Angenommen, ich habe einen Vektor:

std::vector<int> vec;

Ich fülle es mit ein paar Daten. Dann möchte ich einige Iteratoren dazu bringen. Gib sie vielleicht weiter. Vielleicht zu std::for_each:

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

In C++ 03 konnte SomeFunctor den erhaltenen Parameter ändern . Sicher, SomeFunctor könnte seinen Parameter als Wert oder als const& Annehmen, aber es gibt keine Möglichkeit, dies sicherzustellen . Nicht ohne so etwas Dummes zu tun:

const std::vector<int> &vec_ref = vec;
std::for_each(vec_ref.begin(), vec_ref.end(), SomeFunctor());

Nun führen wir cbegin/cend Ein:

std::for_each(vec.cbegin(), vec.cend(), SomeFunctor());

Wir haben jetzt syntaktische Zusicherungen, dass SomeFunctor die Elemente des Vektors nicht ändern kann (natürlich ohne einen Const-Cast). Wir bekommen explizit const_iterator S und daher wird SomeFunctor::operator() mit const int & Aufgerufen. Wenn es die Parameter als int & Annimmt, gibt C++ einen Compilerfehler aus.


C++ 17 hat eine elegantere Lösung für dieses Problem: std::as_const . Nun, zumindest ist es elegant, wenn bereichsbasiertes for verwendet wird:

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

Dies gibt einfach einen const& Für das Objekt zurück, das es bereitgestellt hat.

214
Nicol Bolas

Über das hinaus, was Nicol Bolas in seiner Antwort gesagt hat, betrachten Sie das neue Schlüsselwort auto:

auto iterator = container.begin();

Mit auto kann nicht sichergestellt werden, dass begin() einen konstanten Operator für eine nicht konstante Containerreferenz zurückgibt. Also machst du jetzt:

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

Nehmen Sie dies als praktischen Anwendungsfall

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

Die Zuweisung schlägt fehl, weil it kein Konstanten-Iterator ist. Wenn Sie anfangs cbegin verwendet hätten, hätte der Iterator den richtigen Typ.

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

damit kann ein Programmierer einen const_iterator auch aus einem Nicht-const-Container direkt abrufen

Sie gaben dieses Beispiel

vector<MyType> v;

// fill v ...
typedef vector<MyType>::iterator iter;
for( iter it = v.begin(); it != v.end(); ++it ) {
    // use *it ...
}

Wenn eine Containerüberquerung jedoch nur zur Überprüfung vorgesehen ist, wird im Allgemeinen die Verwendung eines const_iterator bevorzugt, damit der Compiler Verstöße gegen die const-Korrektheit diagnostizieren kann

Beachten Sie, dass im Arbeitspapier auch Adaptervorlagen erwähnt werden, die jetzt als std::begin() und std::end() finalisiert wurden und auch mit nativen Arrays funktionieren. Die entsprechenden std::cbegin() und std::cend() fehlen ab diesem Zeitpunkt merkwürdigerweise, können aber auch hinzugefügt werden.

8
TemplateRex

Bin gerade auf diese Frage gestoßen ... Ich weiß, es ist bereits beantwortet und es ist nur ein Nebenknoten ...

auto const it = container.begin() ist ein anderer Typ als auto it = container.cbegin()

der Unterschied für int[5] (mit Zeiger, von dem ich weiß, dass er nicht die Methode begin hat, sondern den Unterschied gut zeigt ... würde aber in c ++ 14 für std::cbegin() und std::cend(), das ist im Wesentlichen das, was man verwenden sollte, wenn es hier ist) ...

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 und const_iterator haben eine Vererbungsbeziehung und eine implizite Konvertierung findet statt, wenn sie mit dem anderen Typ verglichen oder diesem zugewiesen werden.

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)
{
    // ...
}

Die Verwendung von cbegin() und cend() erhöht in diesem Fall die Leistung.

for (std::vector<T>::const_iterator it=MyVector.cbegin(); it!=MyVector.cend(); ++it)
{
    // ...
}
2
hkBattousai