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 const
variazioni. 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_expr
ed 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_declaration
ed 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_expr
di 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
/ end
metodo 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::begin
non viene chiamato a meno che non range_expression
restituisca un oggetto di tipo namespace std
o 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 __begin
e __end
sono 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; for
loop espanso manualmente.
begin/end
o un amico, statico o gratuitobegin/end
. Basta essere attenti in cui spazio dei nomi si inserisce la funzione libera: stackoverflow.com/questions/28242073/...