2014-04-25 10 views
10

Per quanto ne so, in C++ 11, il riferimento universale deve sempre essere utilizzato con std::forward, ma non sono sicuro di quale tipo di problema può verificarsi se std::forward non viene utilizzato.Che tipo di problemi non vengono inoltrati al riferimento universale?

template <T> 
void f(T&& x); 
{ 
    // What if x is used without std::forward<T>(x) ? 
} 

Potrebbe fornire alcune illustrazioni dei problemi che potrebbero verificarsi in questa situazione?

risposta

16

Non v'è alcun tale regola a sempre uso std::forward con riferimenti universali. Al contrario, può essere pericoloso utilizzare std::forward dappertutto nelle funzioni con i riferimenti universali . Date un'occhiata al seguente esempio:

template <typename T> 
auto make_pair(T&& t) 
{ 
    return std::make_tuple(std::forward<T>(t), std::forward<T>(t)); // BAD 
} 

Se si chiama questa funzione con make_pair(std::string{"foobar"}), il risultato è contro-intuitivo, perché si sposta dallo stesso oggetto due volte.


Aggiornamento: Ecco un altro esempio per mostrare, che fa davvero senso usare riferimenti universali senza perfetta inoltro:

template <typename Range, typename Action> 
void foreach(Range&& range, Action&& action) 
{ 
    using std::begin; 
    using std::end; 
    for (auto p = begin(range), q = end(range); p != q; ++p) { 
     action(*p); 
    } 
} 
  • E 'bene che gamma è un riferimento universale , in modo che il chiamante possa utilizzare foreach con un contenitore temporaneo e un'azione, si chiama una funzione membro non-const sugli elementi.
  • E 'bene che l'azione è un riferimento universale , in modo che il chiamante può passare un'espressione mutabile lambda come azione.
  • E sarebbe errato utilizzare std::forward per intervallo o per azione.
+1

Molto buon punto, ma non chiaramente rispondere alla domanda "che tipo di problema si può verificare se std :: avanti non viene utilizzato?" – Nicolas

+0

+1, ma perché sarebbe sbagliato usare 'std :: forward' per' action'? Ho appena letto questo: [Perché utilizzare un valore perfettamente inoltrato?] (Http://stackoverflow.com/q/24779910/873025), e sembra che tu possa buttare via un'opportunità di ottimizzazione se non preservare la r-valueness. – Snps

+0

@Snps: al contrario di 'std :: apply', il codice sopra riportato contiene un ciclo. A seconda del tipo di 'Azione',' std :: forward (action) 'può essere uguale a' std :: move (action) '. Ciò significa che con 'std :: forward' si cambierebbe potenzialmente l'azione' azione' nella prima iterazione e si causerebbe un comportamento strano per tutte le iterazioni rimanenti. – nosid

9

Diciamo f è chiamato in questo modo:

f(someType{}); 

Se il corpo di f esegue un qualche tipo di operazione su x

foo(x); 

e ci sono due overload per foo

void foo(someType const&); 

void foo(someType&&); 

senza usare std::forward, come x è un lvalue il seguente sovraccarico è chiamato

void foo(someType const&); 

che potrebbe costare un potenziale di ottimizzazione.

Utilizzando

foo(std::forward<T>(x)); 

fa che sia selezionata la corretta sovraccarico foo.

5

Le cose con un nome sono lvalue. Ciò significa che nel corpo della funzione, t è un lvalue. Non importa se il riferimento universale finisce come riferimento di lvalue o come riferimento di rvalore. Se è nominato, è un lvalue.

Se si passa quindi che l'argomento lungo direttamente, si passerà un lvalue.

In una situazione in cui si desidera passare solo lungo un argomento opaco per un'altra funzione, si desidera passare esattamente così com'è. Se era un lvalue, vuoi passarlo come un lvalue; se era un valore massimo, si vuole passare un valore. Come spiegato, passandolo direttamente lo passa come un lvalue sempre. forward fa la magia necessaria per passarlo come il giusto tipo di valore,

Il codice che passa sempre un lvalue probabilmente risentirà di una prospettiva delle prestazioni, ma direi che puoi ignorarlo quando pensi a questo particolare problema. C'è una preoccupazione più pressante per non sempre passando un lvalue: durante la copia di alcuni tipi può essere costoso, alcuni altri tipi non possono essere copiati affatto, come std::unique_ptr. Per quei tipi che preservano la rudimentalità quando l'inoltro non è una questione di prestazioni, ma di ottenere anche la compilazione del codice.

0

non si deve inoltrare una variabile più di una volta.

Problemi correlati