Standard został zmieniony, ponieważ pytanie (i większość odpowiedzi) zostało zamieszczone w rezolucji tego raportu o defektach .
Sposób, aby for(:)
pętla działała na twoim typie, X
jest teraz jednym z dwóch sposobów:
Tworzenie elementu X::begin()
i X::end()
że coś powrotny, który działa jak iteratora
Stwórz darmową funkcję begin(X&)
i end(X&)
że coś powrotny, który działa jak iteratora, w tej samej przestrzeni nazw jako typ X
.¹
I podobne dla const
odmian. Będzie to działać zarówno na kompilatorach, które implementują zmiany raportu defektów, jak i na kompilatorach, które tego nie robią.
Zwrócone obiekty nie muszą tak naprawdę być iteratorami. for(:)
Pętli, w przeciwieństwie do większości części C ++ standardowego jest określony rozszerzyć na coś równoważne :
for( range_declaration : range_expression )
staje się:
{
auto && __range = range_expression ;
for (auto __begin = begin_expr,
__end = end_expr;
__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
gdzie zmienne zaczynające się od __
są tylko dla prezentacji begin_expr
i end_expr
są magią, która wywołuje begin
/ end
.²
Wymagania dotyczące ++
początkowej / końcowej wartości zwracanej są proste: należy przeciążać wstępnie , upewnić się, że wyrażenia inicjujące są poprawne, binarne !=
, których można użyć w kontekście logicznym, jednoargumentowe, *
które zwraca coś, co można przypisać-zainicjować range_declaration
, i ujawnić publiczny burzyciel.
Robienie tego w sposób niezgodny z iteratorem jest prawdopodobnie złym pomysłem, ponieważ przyszłe iteracje C ++ mogą być względnie nonszalanckie jeśli chodzi o złamanie kodu.
Nawiasem mówiąc, istnieje uzasadnione prawdopodobieństwo, że przyszła zmiana standardu pozwoli end_expr
na zwrot innego rodzaju niż begin_expr
. Jest to przydatne, ponieważ pozwala na ocenę „leniwego końca” (jak wykrywanie zerowego zakończenia), którą łatwo zoptymalizować, aby była tak wydajna jak odręcznie napisana pętla C, i inne podobne zalety.
¹ Zauważ, że for(:)
pętle przechowują dowolne pliki tymczasowe w auto&&
zmiennej i przekazują je jako wartość. Nie możesz wykryć, czy iterujesz po tymczasowej (lub innej wartości); takie przeciążenie nie będzie wywoływane przez for(:)
pętlę. Patrz [stmt.ranged] 1.2-1.3 z n4527.
² albo wywołać begin
/ end
lub innej metody ADL tylko odnośników wolnej funkcji begin
/ end
, lub magicznych wsparcia tablicy stylu C. Zauważ, że std::begin
nie jest wywoływany, chyba że range_expression
zwraca obiekt typu namespace std
lub zależny od niego.
W c ++ 17 wyrażenie dla zakresu zostało zaktualizowane
{
auto && __range = range_expression ;
auto __begin = begin_expr;
auto __end = end_expr;
for (;__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
z typami __begin
i __end
zostały oddzielone.
To pozwala, aby iterator końcowy nie był tego samego typu co start. Twój typ iteratora końcowego może być „wartownikiem”, który obsługuje tylko !=
typ iteratora początkowego.
Praktycznym przykładem tego, dlaczego jest to przydatne, jest to, że twój iterator końcowy może przeczytać „sprawdź, char*
czy wskazuje, że '0'
”, gdy ==
jest to char*
. Dzięki temu wyrażenie w zakresie C ++ może generować optymalny kod podczas iteracji w char*
buforze zakończonym zerem.
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;
}
};
przykład na żywo w kompilatorze bez pełnej obsługi C ++ 17; for
pętla rozwinięta ręcznie.
begin/end
lub przyjaciela, statyczne lub wolnebegin/end
. Uważaj tylko, w której przestrzeni nazw umieścisz darmową funkcję: stackoverflow.com/questions/28242073/...