object temporaneo in base all’intervallo

So che, in generale, il tempo di vita di un temporaneo in un ciclo for -range è esteso all’intero ciclo (ho letto C ++ 11: l’istruzione basata su intervallo per la durata di “range-init”? ). Quindi fare cose del genere è generalmente OK:

 for (auto &thingy : func_that_returns_eg_a_vector()) std::cout << thingy; 

Ora sto inciampando sui problemi di memoria quando provo a fare qualcosa che pensavo fosse simile al contenitore QList di Qt:

 #include  #include  int main() { for (auto i : QList{} << 1 << 2 << 3) std::cout << i << std::endl; return 0; } 

Il problema qui è che valgrind mostra l’accesso non valido alla memoria da qualche parte all’interno della class QList . Tuttavia, la modifica dell’esempio in modo che l’elenco sia memorizzato nella variabile fornisce un risultato corretto:

 #include  #include  int main() { auto things = QList{} << 1 << 2 << 3; for (auto i : things) std::cout << i << std::endl; return 0; } 

Ora la mia domanda è: sto facendo qualcosa di stupido nel primo caso, risultando ad esempio un comportamento indefinito (non ho abbastanza esperienza di lettura dello standard C ++ per poter rispondere a me stesso)? Oppure questo è un problema con il modo in cui utilizzo QList o come viene implementato QList ?

Dato che stai usando C ++ 11, potresti usare invece l’elenco di inizializzazione . Questo supererà valgrind:

 int main() { for (auto i : QList{1, 2, 3}) std::cout << i << std::endl; return 0; } 

Il problema non è completamente correlato al range-based o al C ++ 11. Il seguente codice dimostra lo stesso problema:

 QList& things = QList() << 1; things.end(); 

o:

 #include  struct S { int* x; S() { x = NULL; } ~S() { delete x; } S& foo(int y) { x = new int(y); return *this; } }; int main() { S& things = S().foo(2); std::cout << *things.x << std::endl; return 0; } 

La lettura non valida è perché l'object temporaneo dall'espressione S() (o QList{} ) viene distrutto dopo la dichiarazione (seguendo C ++ 03 e C ++ 11 §12.2 / 5), perché il compilatore non ha idea che il metodo foo() (o operator<< ) restituirà quell'object temporaneo. Quindi ora ti riferisci al contenuto della memoria liberata.

Il compilatore non può sapere che il riferimento che è il risultato di tre chiamate operator << è associato all'object temporaneo QList{} , quindi la durata del temporaneo non viene estesa. Il compilatore non sa (e non ci si può aspettare che sappia) qualcosa sul valore di ritorno di una funzione, eccetto il suo tipo. Se si tratta di un riferimento, non sa a cosa potrebbe legarsi. Sono abbastanza sicuro che, per applicare la regola che estende la vita, il legame debba essere diretto.

Questo dovrebbe funzionare perché l'elenco non è più temporaneo:

 #include  #include  int main() { auto things = QList{}; for (auto i : things << 1 << 2 << 3) std::cout << i << std::endl; return 0; } 

E questo dovrebbe funzionare perché l'associazione è diretta, quindi la regola può essere applicata:

 #include  #include  int main() { for (auto i : QList{1, 2, 3}) std::cout << i << std::endl; return 0; }