2012-03-12 1 views
124

ho visto questo qui: Move Constructor calling base-class Move ConstructorQual è la differenza tra std :: movimento e std :: avanti

Qualcuno potrebbe spiegare:

  1. la differenza tra std::move e std::forward, preferibilmente con alcuni esempi di codice?
  2. Come pensarci facilmente, e quando usare quale
+1

Vedere anche queste due domande correlate: [Devo usare std :: move o std :: inoltra in move ctors/assignment operator?] (Http://stackoverflow.com/q/8860233/500104) e [Come funziona std :: forward work?] (http://stackoverflow.com/q/8526598/500104) (come pure la domanda duplicata). – Xeo

+0

"Come pensarci facilmente e quando usare quale" Si usa 'move' quando si desidera * spostare * un valore, e' forward' quando si desidera utilizzare l'inoltro perfetto. Qui non è una scienza missilistica;) –

+0

move() esegui cast incondizionato dove forward() esegue cast in base al parametro passato. –

risposta

115

std::move accetta un oggetto e consente di trattarlo come temporaneo (un valore). Sebbene non sia un requisito semantico, in genere una funzione che accetta un riferimento a un valore rvalore lo invaliderà.Quando vedi std::move, indica che il valore dell'oggetto non dovrebbe essere usato in seguito, ma puoi ancora assegnare un nuovo valore e continuare ad usarlo.

std::forward ha un caso a uso singolo: per eseguire il cast di un parametro di funzione di modello (all'interno della funzione) nella categoria di valori (lvalue o rvalue) utilizzato dal chiamante per passarlo. Ciò consente di inoltrare argomenti rvalue come valori rvalue e valori lvalue da passare come lvalue, uno schema denominato "inoltro perfetto".

Per illustrate:

void overloaded(int const &arg) { std::cout << "by lvalue\n"; } 
void overloaded(int && arg) { std::cout << "by rvalue\n"; } 

template< typename t > 
/* "t &&" with "t" being template param is special, and adjusts "t" to be 
    (for example) "int &" or non-ref "int" so std::forward knows what to do. */ 
void forwarding(t && arg) { 
    std::cout << "via std::forward: "; 
    overloaded(std::forward<t>(arg)); 
    std::cout << "via std::move: "; 
    overloaded(std::move(arg)); // conceptually this would invalidate arg 
    std::cout << "by simple passing: "; 
    overloaded(arg); 
} 

int main() { 
    std::cout << "initial caller passes rvalue:\n"; 
    forwarding(5); 
    std::cout << "initial caller passes lvalue:\n"; 
    int x = 5; 
    forwarding(x); 
} 

Come cita Howard, ci sono somiglianze anche come entrambe queste funzioni gettano semplicemente per tipo di riferimento. Tuttavia, al di fuori di questi casi d'uso specifici (che coprono il 99,9% dell'utilità dei cast di riferimento di rvalue), è necessario utilizzare direttamente static_cast e scrivere una buona spiegazione di ciò che si sta facendo.

+0

Non sono sicuro che sia esatto che il caso d'uso _digitale di std :: forward' sia un perfetto inoltro degli argomenti di funzione. Ho incontrato situazioni in cui voglio trasmettere perfettamente altre cose, come i membri degli oggetti. –

+0

@GeoffRomer Membri di un parametro? Hai un esempio? – Potatoswatter

+0

Ho pubblicato un esempio come domanda a parte: http://stackoverflow.com/questions/20616958/stdforward-without-perfect-forwarding –

19

std::forward viene utilizzato per avanti un parametro esattamente il modo in cui è stato passato a una funzione. Proprio come mostrato qui:

When to use std::forward to forward arguments?

Uso std::move offre un oggetto come rvalue, da abbinare eventualmente un costruttore movimento o una funzione accettare rvalues. Lo fa per std::move(x) anche se x non è un valore di per sé.

51

Entrambi std::forward e std::move non sono altro che calchi.

X x; 
std::move(x); 

Quanto sopra getta l'espressione lvalue x di tipo X per un'espressione rvalue di tipo X (a xValue per essere esatti). move può anche accettare un rvalue:

std::move(make_X()); 

e in questo caso si tratta di una funzione identità: prende un rvalue di tipo X e restituisce un rvalue di tipo X.

Con std::forward è possibile selezionare la destinazione certa misura:

X x; 
std::forward<Y>(x); 

proietta l'espressione lvalue x di tipo X per un'espressione di tipo Y. Esistono restrizioni sulla quale Y può essere.

Y può essere una Base accessibile di X, o un riferimento a una Base di X. Y può essere X o un riferimento a X. Non è possibile scartare qualificatori di cv con forward, ma si può aggiungere cv- qualificazioni. Y non può essere un tipo che è semplicemente convertibile da X, tranne tramite una conversione Base accessibile.

Se Y è un riferimento lvalue, il risultato sarà un'espressione lvalue. Se Y non è un riferimento di lvalue, il risultato sarà un'espressione di valore di rvalue (xvalue per la precisione).

forward può accettare un argomento di rvalue solo se Y non è un riferimento di lvalue. Cioè, non puoi lanciare un valore per lvalue. Questo è per motivi di sicurezza, in quanto in questo modo si ottengono riferimenti ciondolanti. Ma lanciare un valore per rvalue è ok e permesso.

Se si tenta di specificare Y su qualcosa che non è consentito, l'errore verrà rilevato in fase di compilazione, non in fase di esecuzione.

+0

Se inoltro perfettamente un oggetto a una funzione usando 'std :: forward', quindi dopo che la funzione è stata eseguita, posso usare quell'oggetto? Sono consapevole che, in caso di 'std :: move', è un comportamento indefinito. – iammilind

+0

Riguardo a 'move': https://stackoverflow.com/a/7028318/576911 Per' forward', se passi in un lvalue, la tua API dovrebbe reagire come se ricevesse un lvalue. Normalmente questo significa che il valore non sarà modificato. Ma se è un valore non const, la tua API potrebbe averlo modificato. Se si passa un valore rvalore, ciò significa in genere che l'API potrebbe essersi spostato da esso e pertanto si applicherebbe https://stackoverflow.com/a/7028318/576911. –