Qual è la differenza tra std :: move e std :: forward


160

L'ho visto qui: Move Constructor chiama Move Costruttore di classe base

Qualcuno potrebbe spiegare:

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

1
Vedi anche queste due domande correlate: Dovrei usare std :: move o std :: forward in move ctors / operatori di assegnazione? e come funziona std :: forward? (così come la domanda duplicata).
Xeo

"Come pensarci facilmente e quando usare quale" Utilizzare movequando si desidera spostare un valore e forwardquando si desidera utilizzare l'inoltro perfetto. Questa non è scienza missilistica qui;)
Nicol Bolas,

move () esegue il cast incondizionato dove in avanti () esegue il cast in base al parametro passato.
RaGa__M,

Risposte:


159

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

std::forwardha un solo caso d'uso: per lanciare un parametro della funzione modello (all'interno della funzione) nella categoria di valore (lvalue o rvalue) che il chiamante ha usato per passarlo. Ciò consente che gli argomenti rvalue vengano trasmessi come rvalues ​​e che i lvalues ​​vengano trasmessi come lvalues, uno schema chiamato "inoltro perfetto".

Per illustrare :

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 menziona Howard, ci sono anche somiglianze in quanto entrambe queste funzioni si limitano al cast di riferimento. Ma al di fuori di questi casi d'uso specifici (che coprono il 99,9% dell'utilità dei cast di riferimento rvalore), è necessario utilizzare static_castdirettamente e scrivere una buona spiegazione di ciò che si sta facendo.


Non sono sicuro che sia preciso che std::forwardl' unico caso d'uso sia l'inoltro perfetto degli argomenti delle funzioni. Mi sono imbattuto in situazioni in cui voglio inoltrare perfettamente altre cose, come i membri degli oggetti.
Geoff Romer,

@GeoffRomer Membri di un parametro? Hai un esempio?
Potatoswatter l'

Ho pubblicato un esempio come domanda separata: stackoverflow.com/questions/20616958/…
Geoff Romer

Hmm, quindi se lo capisco correttamente, significa che posso scrivere una singola funzione forward () dove forward (5) opera su 5 come se lo passassi per valore dove forward (x) opera su x come se lo avessi passato riferimento senza dover scrivere esplicitamente un sovraccarico.
iheanyi,

@iheanyi Sì, questa è l'idea! Ma deve essere un modello, non solo una normale funzione.
Potatoswatter,

63

Entrambi std::forwarde std::movenon sono altro che cast.

X x;
std::move(x);

Quanto sopra lancia l'espressione lvalue xdi tipo X su un'espressione rvalue di tipo X (un valore x per essere esatti). movepuò anche accettare un valore:

std::move(make_X());

e in questo caso è una funzione di identità: prende un valore di tipo X e restituisce un valore di tipo X.

Con std::forwardte puoi selezionare la destinazione in una certa misura:

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

Lancia l'espressione lvalue xdi tipo X su un'espressione di tipo Y. Esistono vincoli su ciò che 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 eliminare qualificatori cv con forward, ma è possibile aggiungere qualificatori cv. 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 lvalue, il risultato sarà un'espressione rvalue (xvalue per essere precisi).

forwardpuò accettare un argomento rvalue solo se Y non è un riferimento lvalue. Cioè, non puoi lanciare un valore in valore. Questo per motivi di sicurezza in quanto ciò porta comunemente a riferimenti sospesi. Ma lanciare un valore in valore è ok e permesso.

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


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

1
Per quanto riguarda move: stackoverflow.com/a/7028318/576911 Per forward, se passi un valore, la tua API dovrebbe reagire come se ricevesse un valore. Normalmente questo significa che il valore non sarà modificato. Ma se si tratta di un valore non const, l'API potrebbe averlo modificato. Se si passa in un rvalue, questo significa di solito che il vostro API potrebbe aver spostato da esso, e quindi stackoverflow.com/a/7028318/576911 si applicherebbe.
Howard Hinnant,

21

std::forwardviene utilizzato per inoltrare un parametro esattamente come è stato passato a una funzione. Proprio come mostrato qui:

Quando usare std :: forward per inoltrare argomenti?

L'utilizzo std::moveoffre un oggetto come valore, per eventualmente abbinare un costruttore di spostamenti o una funzione che accetta valori. Lo fa std::move(x)anche se xnon è un valore da solo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.