2012-08-17 8 views
155

Ich frage mich, warum cbegin und cend in C++ 11 eingeführt wurden?Was ist der Grund für cbegin/cend?

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

Antwort

179

Es ist ziemlich einfach. Sprich ich habe einen Vektor:

std::vector<int> vec; 

Ich fülle es mit ein paar Daten. Dann möchte ich einige Iteratoren dazu bekommen. Vielleicht kannst du sie herumreichen. Vielleicht std::for_each:

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

In C++ 03, SomeFunctor war frei zu können ändern die Parameter wird es. Sicher, SomeFunctor könnte seinen Parameter nach Wert oder durch const& nehmen, aber es gibt keine Möglichkeit, sicherzustellen, dass es tut. Nicht ohne das etwas albern wie zu tun:

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

Jetzt stellen wir cbegin/cend:

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

Jetzt haben wir syntaktische Versicherungen haben, dass SomeFunctor nicht die Elemente des Vektors (ohne const-Guss ändern können, Na sicher). Wir erhalten explizit const_iterator s, und daher wird SomeFunctor :: operator() mit const int & aufgerufen. Wenn es seine Parameter wie int & nimmt, wird C++ einen Compiler-Fehler ausgeben.


C++ 17 hat eine elegantere Lösung für dieses Problem: std::as_const. Nun, zumindest ist es elegant bei der Verwendung von bereichsbasierte for:

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

Dieses einfach eine const& auf das Objekt gibt es vorgesehen ist.

+1

Ich dachte, das neue Protokoll wurde cbegin (vec) und nicht vec.cbegin(). –

+18

@Kaz: Es gibt keine 'std :: cbegin/cend' free-Funktionen, wie' std :: begin/std :: end' existiert. Es war ein Versehen des Ausschusses. Wenn diese Funktionen existieren würden, wäre das im Allgemeinen der Weg, sie zu verwenden. –

+15

Anscheinend wird 'std :: cbegin/cend' in C++ 14 hinzugefügt. Siehe http://en.cppreference.com/w/cpp/iterator/begin –

8

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

so dass ein Programmierer direkt eine const_iterator von sogar einem nicht konstanten Container

vector<MyType> v; 

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

jedoch dieses Beispiel Sie gaben erhalten , wenn eine Behälterdurchfahrt nur zur Inspektion vorgesehen ist, es ist eine allgemein bevorzugte Praxis eine const_iterator zu verwenden, um die Compiler ermöglichen const-Korrektheit Verletzungen

Beachten Sie, dass das Arbeitspapier auch erwähnt Adapter-Vorlagen, die jetzt abgeschlossen wurden als std::begin() und std::end() zu diagnostizieren und zu das funktioniert auch mit nativen Arrays. Die entsprechenden std::cbegin() und std::cend() sind zu dieser Zeit seltsamerweise verschwunden, aber sie könnten auch hinzugefügt werden.

60

jenseits dessen, was Nicol Bolas in his answer sagte, die neue auto Schlüsselwort berücksichtigen:

auto iterator = container.begin(); 

Mit auto, gibt es keinen Weg, um sicher zu stellen, dass begin() einen konstanten Operator für einen nicht-ständigen Behälter Referenz zurückgibt. So, jetzt Sie tun:

auto const_iterator = container.cbegin(); 
+0

Konnte nicht const auto const_iterator = container.begin()? – allyourcode

+1

@allyourcode: Hilft nicht. Für den Compiler ist "const_iterator" nur ein weiterer Bezeichner. Keine der beiden Versionen verwendet eine Suche nach den üblichen Membertypdefinitionen 'declltype (container) :: iterator' oder' declltype (container) :: const_iterator'. – aschepler

+1

@aschepler Ich verstehe deinen zweiten Satz nicht, aber ich glaube, du hast die "const" vor "auto" in meiner Frage verpasst. Was auch immer auto kommt, scheint, dass const_iterator const sein sollte. – allyourcode

13

Nehmet als praktisches usecase

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

Die Zuordnung schlägt fehl, weil it ein nonconst Iterator ist. Wenn Sie anfänglich cbegin verwendet hätten, hätte der Iterator den richtigen Typ.

3

Nur auf diese Frage gestolpert ... Ich weiß, es alredy answerd und es ist nur ein Nebenknoten ...

auto const it = container.begin() ist eine andere Art dann auto it = container.cbegin()

die Differenz für int[5] (Zeiger verwenden, die ich weiß nicht, das Verfahren beginnen, sondern zeigt schön den Unterschied ... aber würde in C++ 14 für std::cbegin() und std::cend(), die im wesentlichen ist, was sollte man verwenden, wenn es hier) ...

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 
1

iterator und const_iterator haben eine Vererbungsbeziehung und eine implizite Konvertierung tritt auf, wenn sie mit dem anderen Typ verglichen oder zugewiesen wird.

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

Mit cbegin() und cend() wird die Leistung in diesem Fall erhöhen.

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