C’è qualche caso in cui un ritorno di un riferimento RValue (&&) è utile?

C’è una ragione per cui una funzione dovrebbe restituire un riferimento RValue ? Una tecnica, o un trucco, o un idioma o uno schema?

MyClass&& func( ... ); 

Sono consapevole del pericolo di restituire riferimenti in generale, ma a volte lo facciamo comunque, no ( T& T::operator=(T) è solo un esempio idiomatico). Ma che ne pensi di T&& func(...) ? C’è un posto generale in cui avremmo guadagnato dal farlo? Probabilmente diverso quando si scrive una libreria o un codice API, rispetto al solo codice cliente?

Ci sono alcune occasioni in cui è appropriato, ma sono relativamente rari. Il caso si presenta in un esempio quando si desidera consentire al client di spostarsi da un membro dati. Per esempio:

 template  class move_iterator { private: Iter i_; public: ... value_type&& operator*() const {return std::move(*i_);} ... }; 

Questo fa seguito al commento di towi. Non si desidera mai restituire riferimenti a variabili locali. Ma potresti avere questo:

 vector operator+(const vector& x1, const vector& x2) { vector x3 = x1; x3 += x2; return x3; } vector&& operator+(const vector& x1, vector&& x2) { x2 += x1; return std::move(x2); } vector&& operator+(vector&& x1, const vector& x2) { x1 += x2; return std::move(x1); } vector&& operator+(vector&& x1, vector&& x2) { x1 += x2; return std::move(x1); } 

Ciò dovrebbe impedire qualsiasi copia (e possibili allocazioni) in tutti i casi, tranne nel caso in cui entrambi i parametri siano lvalue.

No. Basta restituire il valore. Restituire i riferimenti in generale non è affatto pericoloso: restituisce riferimenti a variabili locali che sono pericolosi. Restituire un riferimento rvalue, tuttavia, è abbastanza inutile in quasi tutte le situazioni (suppongo che stiate scrivendo std::move o qualcosa del genere).

Puoi tornare per riferimento se sei sicuro che l’object di riferimento non uscirà dall’ambito dopo che la funzione è stata chiusa, ad esempio è un riferimento a un object globale o una funzione membro che restituisce il riferimento ai campi di class, ecc.

Questa regola di riferimento di ritorno è la stessa sia per lvalue che per riferimento di rvalue. La differenza è come si desidera utilizzare il riferimento restituito. Come posso vedere, il ritorno del riferimento di rvalue è raro. Se hai una funzione:

 Type&& func(); 

Non ti piacerà tale codice:

 Type&& ref_a = func(); 

perché definisce in modo efficace ref_a come Tipo e dato che il riferimento al valore nominale è un lvalue, e qui non verrà eseguita alcuna mossa effettiva. È piuttosto come:

 const Type& ref_a = func(); 

tranne che il ref_a effettivo è un riferimento a valore non costante.

Inoltre, non è molto utile nemmeno se si passa direttamente func () ad un’altra funzione che accetta un argomento Type && perché è ancora un riferimento con nome all’interno di quella funzione.

 void anotherFunc(Type&& t) { // t is a named reference } anotherFunc(func()); 

La relazione di func () e anotherFunc () è più simile a una “authorization” che func () accetta che anotherFunc () possa assumere la proprietà di (o si può dire “rubare”) l’object restituito da func (). Ma questo accordo è molto lento. Un riferimento a valore non costante può ancora essere “rubato” dai chiamanti. In realtà le funzioni sono raramente definite per prendere argomenti di riferimento di rvalore. Il caso più comune è che “anotherFunc” è un nome di class e anotherFunc () è in realtà un costruttore di movimento.

Un altro ansible caso: quando è necessario decomprimere una tupla e passare i valori a una funzione.

Potrebbe essere utile in questo caso, se non sei sicuro di copy-elision.

Un esempio del genere:

 template class store_args{ public: std::tuple args; template decltype(auto) apply_helper(Functor &&f, std::integer_sequence&&){ return std::move(f(std::forward(std::get(args))...)); } template auto apply(Functor &&f){ return apply_helper(std::move(f), std::make_index_sequence{}); } }; 

caso piuttosto raro a meno che tu non stia scrivendo qualche forma di std::bind o std::thread sostituzione del std::thread .