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
?
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
?
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.
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.
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();
Konnte nicht const auto const_iterator = container.begin()? – allyourcode
@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
@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
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.
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
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)
{
// ...
}
Ich dachte, das neue Protokoll wurde cbegin (vec) und nicht vec.cbegin(). –
@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. –
Anscheinend wird 'std :: cbegin/cend' in C++ 14 hinzugefügt. Siehe http://en.cppreference.com/w/cpp/iterator/begin –