Se si guarda get
, la funzione di supporto per std::tuple
, si noterà il seguente overload:Perché fa arrivare aiutante di std :: riferimento rvalue ritorno tupla invece di valore
template< std::size_t I, class... Types >
constexpr std::tuple_element_t<I, tuple<Types...> >&&
get(tuple<Types...>&& t);
In altre parole, restituisce un riferimento di rvalue quando la tupla di input è un riferimento di rvalue stesso. Perché non restituire in base al valore, chiamando move
nel corpo della funzione? Il mio argomento è il seguente: il ritorno di get sarà legato a un riferimento oa un valore (potrebbe non essere associato a nulla, suppongo, ma questo non dovrebbe essere un caso d'uso comune). Se è legato a un valore, si verificherà comunque una costruzione di movimento. Quindi non perdi nulla restituendo valore. Se ci si lega a un riferimento, quindi la restituzione di un riferimento rvalue può effettivamente essere non sicura. Per mostrare un esempio:
struct Hello {
Hello() {
std::cerr << "Constructed at : " << this << std::endl;
}
~Hello() {
std::cerr << "Destructed at : " << this << std::endl;
}
double m_double;
};
struct foo {
Hello m_hello;
Hello && get() && { return std::move(m_hello); }
};
int main() {
const Hello & x = foo().get();
std::cerr << x.m_double;
}
Quando viene eseguito, questo programma stampa:
Constructed at : 0x7ffc0e12cdc0
Destructed at : 0x7ffc0e12cdc0
0
In altre parole, x è un riferimento immediatamente penzoloni. Se invece hai appena scritto foo in questo modo:
struct foo {
Hello m_hello;
Hello get() && { return std::move(m_hello); }
};
Questo problema non si verificherebbe. Inoltre, se si quindi utilizzare foo in questo modo:
Hello x(foo().get());
Non sembra che ci sia alcun overhead aggiuntivo se si ritorna per valore, o riferimento rvalue. Ho testato il codice in questo modo e sembra che eseguirà in modo coerente solo una singola costruzione di movimento. Per esempio. se posso aggiungere un membro:
Hello(Hello &&) { std::cerr << "Moved" << std::endl; }
E io costruisco x come sopra, il mio programma di stampa solo "spostato" una volta indipendentemente dal fatto che torno per valore o riferimento rvalue.
C'è una buona ragione che mi manchi o è una svista?
Nota: c'è una buona domanda correlata qui: Return value or rvalue reference?. Sembra che il ritorno di valore sia generalmente preferibile in questa situazione, ma il fatto che si presenti nell'STL mi fa incuriosire se l'AWL abbia ignorato questo ragionamento, o se hanno motivi speciali che potrebbero non essere applicabili. generalmente.
Modifica: Qualcuno ha suggerito che questa domanda sia una copia di Is there any case where a return of a RValue Reference (&&) is useful?. Questo non è il caso; questa risposta suggerisce il ritorno per riferimento di rvalue come metodo per elidere la copia dei membri dei dati. Come discusso in dettaglio sopra, la copiatura verrà eliminata se si restituisce per valore o valore di riferimento, a condizione che si chiami prima move
.
Questo non è un duplicato in remoto. La mia risposta esplicitamente e in dettaglio discute su come il ritorno di valore e la chiamata a std :: move anticipatamente ottengano lo stesso effetto dello spostamento da un membro di dati. La mia domanda è legata alle differenze nei due approcci. La domanda che ho citato è più correlata alla mia domanda rispetto a quella che hai citato. Si prega di leggere più attentamente prima di correre a etichettare qualcosa di duplicato. –
se si restituisce per valore - si crea una copia. se lo sposti, ne sposti una copia. se si restituisce un valore-riferimento-r, è possibile spostarlo, quindi l'oggetto originale viene spostato o non gestirlo con le semoventi move, quindi si comporterà come riferimento regolare. il punto è che se si restituisce un riferimento al valore r non si forza la copia. –
Perché richiedere la costruibilità del movimento? – Columbo