Antworten:
Es ist ganz einfach. Angenommen, ich habe einen Vektor:
std::vector<int> vec;
Ich fülle es mit einigen Daten. Dann möchte ich ein paar Iteratoren dazu bringen. Vielleicht geben Sie sie weiter. Vielleicht zu std::for_each:
std::for_each(vec.begin(), vec.end(), SomeFunctor());
In C ++ 03 SomeFunctorwar es frei, den erhaltenen Parameter ändern zu können . Sicher, SomeFunctorkönnte seinen Parameter nach Wert oder nach nehmen const&, 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 stellen wir vor cbegin/cend:
std::for_each(vec.cbegin(), vec.cend(), SomeFunctor());
Jetzt haben wir syntaktische Zusicherungen, SomeFunctordie die Elemente des Vektors nicht ändern können (natürlich ohne const-cast). Wir bekommen explizit const_iterators und werden daher SomeFunctor::operator()mit aufgerufen const int &. Wenn die Parameter als verwendet werden int &, gibt C ++ einen Compilerfehler aus.
C ++ 17 bietet eine elegantere Lösung für dieses Problem : std::as_const. Nun, zumindest ist es elegant, wenn man Range-basiert verwendet for:
for(auto &item : std::as_const(vec))
Dies gibt einfach ein const&an das bereitgestellte Objekt zurück.
std::cbegin/cendfreien Funktionen, wie sie std::begin/std::endexistieren. Es war ein Versehen des Komitees. Wenn diese Funktionen vorhanden wären, würden sie im Allgemeinen so verwendet.
std::cbegin/cendwird in C ++ 14 hinzugefügt. Siehe en.cppreference.com/w/cpp/iterator/begin
for(auto &item : std::as_const(vec))entspricht for(const auto &item : vec)?
constauf die Referenz setzen. Nicol betrachtet den Container als const und autoleitet daher eine constReferenz ab. IMO auto const& itemist einfacher und klarer. Es ist unklar, warum std::as_const()hier gut ist; Ich kann sehen, dass es nützlich wäre, wenn etwas nicht constan generischen Code übergeben wird, bei dem wir den verwendeten Typ nicht steuern können, aber mit Range forkönnen wir das. Es scheint mir also nur ein zusätzliches Rauschen zu sein.
Betrachten Sie über das hinaus, was Nicol Bolas in seiner Antwort gesagt hat , das neue autoSchlüsselwort:
auto iterator = container.begin();
Mit autokann nicht sichergestellt werden, dass begin()ein konstanter Operator für eine nicht konstante Containerreferenz zurückgegeben wird. Jetzt machst du also:
auto const_iterator = container.cbegin();
const_iteratorist dies nur eine weitere Kennung. Keine der Versionen verwendet eine Suche nach den üblichen Mitgliedertypedefs decltype(container)::iteratoroder decltype(container)::const_iterator.
const_iteratormit erhalten auto: Schreiben Sie eine Hilfsfunktionsvorlage, die aufgerufen wird make_const, um das Objektargument zu qualifizieren.
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, da ites sich um einen nicht konstanten Iterator handelt. Wenn Sie anfänglich cbegin verwendet hätten, hätte der Iterator den richtigen Typ gehabt.
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, ist es allgemein bevorzugt, einen const_iterator zu verwenden, damit der Compiler Verstöße gegen die Konstanzkorrektheit diagnostizieren kann
Beachten Sie, dass im Arbeitspapier auch Adaptervorlagen erwähnt werden, die jetzt als std::begin()und finalisiert wurden std::end()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.
Ich bin gerade auf diese Frage gestoßen ... Ich weiß, dass sie bereits beantwortet wurde und nur ein Seitenknoten ist ...
auto const it = container.begin() ist dann ein anderer Typ auto it = container.cbegin()
der Unterschied für int[5](mit Zeiger, von dem ich weiß, dass er nicht die Methode begin hat, aber den Unterschied gut zeigt ... aber in c ++ 14 für std::cbegin()und funktionieren würde std::cend(), was man im Wesentlichen verwenden sollte, wenn er 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
iteratorund const_iteratorVererbungsbeziehung haben und eine implizite Konvertierung erfolgt, wenn sie mit dem anderen Typ verglichen oder diesem 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)
{
// ...
}
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)
{
// ...
}
constHauptvorteil die Leistung ist (was nicht der Fall ist : semantisch korrekter und sicherer Code). Aber während Sie einen Punkt haben, macht (A) autodas zu einem Nicht-Problem; (B) Wenn Sie über die Leistung sprechen, haben Sie eine wichtige Sache übersehen, die Sie hier hätten tun sollen: Zwischenspeichern Sie den endIterator, indem Sie eine Kopie davon im Init-Zustand der forSchleife deklarieren , und vergleichen Sie diese, anstatt eine neue Kopie von zu erhalten Wert für jede Iteration. Das wird Ihren Standpunkt verbessern. : P
constkann definitiv dazu beitragen, eine bessere Leistung zu erzielen, nicht aufgrund von Magie im constSchlüsselwort selbst, sondern weil der Compiler einige Optimierungen aktivieren kann, wenn er weiß, dass die Daten nicht geändert werden, was sonst nicht möglich wäre. Schauen Sie sich dieses Stück aus einem Vortrag von Jason Turner an, um ein Live-Beispiel dafür zu erhalten.
constdies (fast indirekt) zu Leistungsvorteilen führen kann. Nur für den Fall, dass jemand, der dies liest, denkt: "Ich werde mich nicht darum kümmern, etwas hinzuzufügen, constwenn der generierte Code in keiner Weise beeinflusst wird", was nicht stimmt.
Sein einfacher cbegin gibt einen konstanten Iterator zurück, wobei begin nur einen Iterator zurückgibt
Zum besseren Verständnis nehmen wir hier zwei Szenarien
Szenario 1 :
#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;
for (int i = 1; i < 6; ++i)
{
/* code */
v.push_back(i);
}
for(auto i = v.begin();i< v.end();i++){
*i = *i + 5;
}
for (auto i = v.begin();i < v.end();i++){
cout<<*i<<" ";
}
return 0;
}
Dies wird ausgeführt, da hier der Iterator i nicht konstant ist und um 5 erhöht werden kann
Verwenden wir nun cbegin und cend, um sie als konstantes Iteratorszenario zu bezeichnen - 2:
#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;
for (int i = 1; i < 6; ++i)
{
/* code */
v.push_back(i);
}
for(auto i = v.cbegin();i< v.cend();i++){
*i = *i + 5;
}
for (auto i = v.begin();i < v.end();i++){
cout<<*i<<" ";
}
return 0;
}
Dies wird nicht funktionieren, da Sie den Wert nicht mit cbegin und cend aktualisieren können, wodurch der konstante Iterator zurückgegeben wird