Der Standard wurde geändert, seit die Frage (und die meisten Antworten) in der Lösung dieses Fehlerberichts veröffentlicht wurden .
Es gibt zwei Möglichkeiten, wie Sie eine for(:)
Schleife für Ihren Typ verwenden X
können:
Erstellen Sie Mitglied X::begin()
und X::end()
dass die Rückkehr etwas , das wie ein Iterator wirkt
Erstellen Sie eine freie Funktion begin(X&)
und end(X&)
dass die Rückkehr etwas , das wie ein Iterator fungiert, im gleichen Namensraum wie Ihre Art X
.¹
Und ähnlich für const
Variationen. Dies funktioniert sowohl bei Compilern, die die Änderungen des Fehlerberichts implementieren, als auch bei Compilern, die dies nicht tun.
Die zurückgegebenen Objekte müssen keine Iteratoren sein. Die for(:)
Schleife wird im Gegensatz zu den meisten Teilen des C ++ - Standards so spezifiziert , dass sie wie folgt erweitert wird :
for( range_declaration : range_expression )
wird:
{
auto && __range = range_expression ;
for (auto __begin = begin_expr,
__end = end_expr;
__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
wobei die Variablen, die mit beginnen, __
nur zur Darstellung dienen begin_expr
und end_expr
die Magie ist, die begin
/ end
.² aufruft
Die Anforderungen an den Rückgabewert für Anfang / Ende sind einfach: Sie müssen vorab überladen ++
, sicherstellen, dass die Initialisierungsausdrücke gültig sind, binär !=
, die in einem booleschen Kontext verwendet werden können, unär *
, was etwas zurückgibt, mit dem Sie initialisieren können range_declaration
, und eine Öffentlichkeit verfügbar machen Zerstörer.
Dies auf eine Weise zu tun, die nicht mit einem Iterator kompatibel ist, ist wahrscheinlich eine schlechte Idee, da zukünftige Iterationen von C ++ möglicherweise relativ unbekümmert sind, wenn Sie Ihren Code brechen, wenn Sie dies tun.
Abgesehen davon ist es ziemlich wahrscheinlich, dass eine zukünftige Überarbeitung des Standards die end_expr
Rückgabe eines anderen Typs als ermöglicht begin_expr
. Dies ist insofern nützlich, als es eine "Lazy-End" -Auswertung (wie das Erkennen einer Nullterminierung) ermöglicht, die leicht zu optimieren ist, um so effizient wie eine handgeschriebene C-Schleife zu sein, und andere ähnliche Vorteile.
¹ Beachten Sie, dass for(:)
Schleifen alle temporären auto&&
Elemente in einer Variablen speichern und als l-Wert an Sie übergeben. Sie können nicht erkennen, ob Sie über einen temporären (oder einen anderen Wert) iterieren. Eine solche Überlastung wird von einer for(:)
Schleife nicht aufgerufen . Siehe [stmt.ranged] 1.2-1.3 von n4527.
² entweder den Anruf begin
/ eine end
Methode oder ADL-only lookup freier Funktion begin
/ end
, oder Magie für C-style - Array - Unterstützung. Beachten Sie, dass dies std::begin
nur aufgerufen wird, wenn range_expression
ein Objekt vom Typ in namespace std
dasselbe zurückgegeben wird oder von diesem abhängig ist.
Im c ++ 17 Der Range-for-Ausdruck wurde aktualisiert
{
auto && __range = range_expression ;
auto __begin = begin_expr;
auto __end = end_expr;
for (;__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
mit den Arten von __begin
und __end
wurden entkoppelt.
Dadurch kann der Enditerator nicht vom selben Typ wie begin sein. Ihr Enditeratortyp kann ein "Sentinel" sein, der nur !=
mit dem Anfangsiteratortyp unterstützt wird.
Ein praktisches Beispiel dafür , warum dies nützlich ist , dass Ihr Ende kann Iterator „überprüfen Sie Ihre lesen , char*
um zu sehen , ob es weist auf '0'
“ , wenn ==
mit ein char*
. Auf diese Weise kann ein C ++ - Bereichsausdruck optimalen Code generieren, wenn er über einen nullterminierten char*
Puffer iteriert .
struct null_sentinal_t {
template<class Rhs,
std::enable_if_t<!std::is_same<Rhs, null_sentinal_t>{},int> =0
>
friend bool operator==(Rhs const& ptr, null_sentinal_t) {
return !*ptr;
}
template<class Rhs,
std::enable_if_t<!std::is_same<Rhs, null_sentinal_t>{},int> =0
>
friend bool operator!=(Rhs const& ptr, null_sentinal_t) {
return !(ptr==null_sentinal_t{});
}
template<class Lhs,
std::enable_if_t<!std::is_same<Lhs, null_sentinal_t>{},int> =0
>
friend bool operator==(null_sentinal_t, Lhs const& ptr) {
return !*ptr;
}
template<class Lhs,
std::enable_if_t<!std::is_same<Lhs, null_sentinal_t>{},int> =0
>
friend bool operator!=(null_sentinal_t, Lhs const& ptr) {
return !(null_sentinal_t{}==ptr);
}
friend bool operator==(null_sentinal_t, null_sentinal_t) {
return true;
}
friend bool operator!=(null_sentinal_t, null_sentinal_t) {
return false;
}
};
Live-Beispiel in einem Compiler ohne vollständige C ++ 17-Unterstützung; for
Schleife manuell erweitert.
begin/end
oder einen Freund zu definieren, statisch oder freibegin/end
. Seien