Lo standard è stato modificato da quando la domanda (e la maggior parte delle risposte) sono state pubblicate nella risoluzione di questo rapporto sui difetti .
Il modo per far funzionare un for(:)ciclo sul tuo tipo Xè ora in due modi:
Crea membro X::begin()e X::end()restituisci qualcosa che si comporta come un iteratore
Crea una funzione gratuita begin(X&)e end(X&)restituisci qualcosa che si comporta come un iteratore, nello stesso spazio dei nomi del tuo tipo X.¹
E simile per le constvariazioni. Questo funzionerà sia sui compilatori che implementano le modifiche al rapporto sui difetti, sia sui compilatori che non lo fanno.
Gli oggetti restituiti non devono essere effettivamente iteratori. Il for(:)ciclo, a differenza della maggior parte delle parti dello standard C ++, è specificato per espandersi in qualcosa di equivalente a :
for( range_declaration : range_expression )
diventa:
{
auto && __range = range_expression ;
for (auto __begin = begin_expr,
__end = end_expr;
__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
dove le variabili che iniziano con __sono solo per esposizione, begin_expred end_exprè la magia che chiama begin/ end.²
I requisiti sul valore di ritorno inizio / fine sono semplici: è necessario sovraccaricare pre ++, assicurarsi che le espressioni di inizializzazione siano valide, binarie !=che possano essere utilizzate in un contesto booleano, unarie *che restituiscano qualcosa che è possibile assegnare-inizializzare range_declarationed esporre un pubblico distruttore.
Farlo in un modo che non è compatibile con un iteratore è probabilmente una cattiva idea, poiché le future iterazioni di C ++ potrebbero essere relativamente sprezzanti sulla violazione del codice, se lo fai.
A parte questo, è ragionevolmente probabile che una futura revisione dello standard consentirà end_exprdi restituire un tipo diverso da begin_expr. Ciò è utile in quanto consente una valutazione "lazy-end" (come il rilevamento della terminazione nulla) che è facile da ottimizzare per essere efficiente come un ciclo C scritto a mano e altri vantaggi simili.
¹ Nota che i for(:)loop memorizzano qualsiasi temporaneo in una auto&&variabile e te lo passano come un valore. Non è possibile rilevare se si sta ripetendo un temporaneo (o un altro valore); un tale sovraccarico non verrà chiamato da un for(:)loop. Vedi [stmt.ranged] 1.2-1.3 da n4527.
² O chiamare il begin/ endmetodo o ADL di sola ricerca del libero funzione di begin/ end, o la magia per il supporto serie in stile C. Si noti che std::beginnon viene chiamato a meno che non range_expressionrestituisca un oggetto di tipo namespace stdo dipendente dallo stesso.
Nel c ++ 17 l'intervallo per l'espressione è stato aggiornato
{
auto && __range = range_expression ;
auto __begin = begin_expr;
auto __end = end_expr;
for (;__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
con i tipi di __begine __endsono stati disaccoppiati.
Ciò consente all'iteratore finale di non essere dello stesso tipo di inizio. Il tipo di iteratore finale può essere una "sentinella" che supporta solo !=con il tipo di iteratore iniziale.
Un esempio pratico del perché questo è utile è che il tuo iteratore finale può leggere "controlla il tuo char*per vedere se punta a '0'" quando ==con a char*. Ciò consente a un intervallo C ++ per l'espressione di generare codice ottimale durante l'iterazione su un char*buffer con terminazione null .
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;
}
};
esempio live in un compilatore senza pieno supporto C ++ 17; forloop espanso manualmente.
begin/endo un amico, statico o gratuitobegin/end. Basta essere attenti in cui spazio dei nomi si inserisce la funzione libera: stackoverflow.com/questions/28242073/...