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, Xjest 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 constodmian. 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_expri end_exprsą 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_exprna 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/ endlub innej metody ADL tylko odnośników wolnej funkcji begin/ end, lub magicznych wsparcia tablicy stylu C. Zauważ, że std::beginnie jest wywoływany, chyba że range_expressionzwraca obiekt typu namespace stdlub 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 __begini __endzostał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; forpętla rozwinięta ręcznie.
begin/endlub przyjaciela, statyczne lub wolnebegin/end. Uważaj tylko, w której przestrzeni nazw umieścisz darmową funkcję: stackoverflow.com/questions/28242073/...