2012-04-03 10 views
12

This question ha menzionato l'uso ovvio e idiomatico di C++ 11 basato su intervallo.Lo stile corretto per la dichiarazione in base all'intervallo per

for (auto& elem: container) { 
    // do something with elem 
} 

Ho avuto dubbi sul tipo di riferimento che si suppone di utilizzare, però. Gli iteratori di input possono restituire rvalue. Sebbene il tipo implicito introdotto da auto possa essere dedotto a const che si legherebbe a un valore di rvalore, ciò non sembra verificarsi.

È la migliore prassi generale per utilizzare l'inoltro perfetto?

for (auto && elem: container) { 
    // do something with elem 
} 

Non vedo il lato negativo qui, ma sembra un po 'troppo carino. Forse non ho ancora scritto abbastanza C++ 11.

+0

Per gli utenti MSVC10 poveri a cui non è possibile usufruire dell'intervallo, la stessa domanda si applica a "BOOST_FOREACH'. –

+0

'auto' non è mai' const'. Devi dire 'auto const &'. Inoltre, 'auto &&' non è un riferimento di rvalue, ma più di un" riferimento universale ". –

+0

@KerrekSB Buone informazioni che 'const' non è dedotto lì. Non ho mai detto nulla sui riferimenti rvalue; v) – Potatoswatter

risposta

6

In primo luogo, alcuni consigli generali su come utilizzare auto che non è specifico per forchetta. auto&& può essere problematico se l'inizializzatore è un valore x riferito a un valore temporaneo, poiché in questo caso l'estensione della durata non può essere applicata. Per dirla più semplicemente, e con il codice:

// Pass-through identity function that doesn't construct objects 
template<typename T> 
T&& 
id(T&& t) 
{ return std::forward<T>(t); } 

// Ok, lifetime extended 
// T {} is a prvalue 
auto&& i = T {}; 

T* address = &i; 

// Still ok: lifetime of the object referred to by i exceed that of j 
// id(whatever) is an xvalue 
auto&& j = id(std::move(i)); 

// No other object is involved or were constructed, 
// all those references are bound to the same object 
assert(&j == address); 

// Oops, temporary expires at semi-colon 
// id(whatever) is an xvalue, again 
auto&& k = id(T {}); 

La grande indizio che ci sia qualcosa di losco succedendo qui è che id ha tipo T&& tornare. Se restituisse T allora id(whatever) sarebbe un valore di prvalore e il temporaneo restituito avrebbe avuto la sua durata estesa (tuttavia ciò implicherebbe una costruzione).


Con quella di mezzo, quando si tratta di gamma-for anche se bisogna ricordare che for(auto&& ref: init) { /* body */ } è specificato di essere più o meno equivalente al seguente (ignorando alcuni dettagli che non contano qui):

{ 
    using std::begin; 
    using std::end; 
    auto&& range = init; 
    for(auto b = begin(range), e = end(range); b != e; ++b) { 
     auto&& ref = *b; 
     /* body */ 
    } 
} 

Dobbiamo chiederci ora, che cosa se *b è un xValue (cioè il tipo iteratore ha un operator* ritorno value_type&&, come è il caso ad esempio con std::move_iterator<Iterator>)? Deve quindi fare riferimento a un oggetto che corrisponderà a outlive ref, poiché la riga auto&& ref = *b; non prevede alcun intervento temporaneo. Quindi è sicuro. Altrimenti, se *b è un valore nominale (ad esempio il tipo di iteratore ha un operator* che restituisce T per un tipo di oggetto T), la durata del temporaneo viene estesa per il resto del corpo del ciclo. In tutti i casi sei al sicuro (il caso in cui *b è un lvalue lasciato come esercizio al lettore).

Io personalmente faccio un uso intenso di auto&&, con o senza intervallo-per. Ma mi chiedo ogni volta se l'inizializzatore è un valore x oppure no, e se lo è, qual è la durata di ciò a cui ci si riferisce.

+0

Buone informazioni. Un'altra prospettiva ... l'unico temporaneo in cui una funzione può restituire un valore x è un argomento rvalue. 'begin' e' end' non accettano argomenti, quindi l'inoltro dei risultati è sicuro. – Potatoswatter

+0

@Potatoswatter In questo caso è '* b' che è di interesse (e che in effetti non prende argomenti, argomento molto meno temporaneo come hai giustamente analizzato). 'begin' e' end' prendono argomenti, ma funzionano solo con lvalue. –

+0

Sì, mi riferivo ai membri 'begin' e' end' * *, escludendo 'this' perché è un lvalue. 'operator *' prende un argomento essenzialmente nello stesso senso di 'begin' e' end'. Ma sì, è quello che conta davvero. – Potatoswatter

Problemi correlati