2015-10-16 17 views
5

Quando ho un codice che è simile al seguente:Perfetto inoltro e modelli

template<class T> 
void f_(const T& arg) 
{ 
    cout << "void f(const T& arg): Cannot modify\n"; 
} 

template<class T> 
void f_(T&& arg) 
{ 
    cout << "void f(T&& arg): Can modify\n"; 
} 

e principale per cui ho chiamarlo:

int main() 
{ 

    MemoryBlock block; 
    f_(block); 
    f_(MemoryBlock()); 
    return 0; 
} 

L'output è:
"void f (T & & arg): Può modificare \ n ";
"void f (T & & arg): Può modificare \ n";

Ma quando cambio questo codice per non generico, cioè invece di modelli di funzione avrò funzioni regolari,

void f(const MemoryBlock&) 
{ 
    cout << "In f(const MemoryBlock&). This version cannot modify the parameter.\n"; 
} 

void f(MemoryBlock&&) 
{ 
    cout << "In f(MemoryBlock&&). This version can modify the parameter.\n"; 
} 

l'uscita è più "intuitivo":
"In f (const MemoryBlock &). Questa versione non può modificare il parametro. ";
"In f (MemoryBlock & &). Questa versione può modificare il parametro.";

Mi sembra che solo cambiando le funzioni da modelli a non modelli cambia totalmente le regole di detrazione per i riferimenti di valore.
Sarà davvero buono se qualcuno me lo spiegasse.

+1

tuo indurre in errore pensando che il parametro template ' T' esegue il mapping a 'MemoryBlock'. – Walter

+0

È interessante che tu abbia un "inoltro perfetto" nel titolo, ma non esegui alcun inoltro. – Mehrdad

risposta

4

Quando si utilizza T&&, questo non è un riferimento di rvalue, è un parametro di riferimento universale. Sono dichiarati allo stesso modo, ma si comportano diversamente.

Quando si rimuovono i parametri del modello, non ci si trova più in un contesto deducibile ed è in realtà un riferimento di rvalue: si collegano solo ai valori rval, ovviamente.

In un contesto deducibile (ovvero quando si sta effettuando la detrazione del tipo), T&& può essere un riferimento di valore o un riferimento di lvalue. I riferimenti universali possono associarsi a praticamente tutte le combinazioni (const, const volatile, ecc.) E nel tuo caso: const T&.

Ora il tuo treno di pensiero doveva essere efficiente e sovraccarico per rvalues ​​e poi lvalues, ma ciò che accade è che il sovraccarico universale di riferimento è una corrispondenza migliore quando si deducono gli argomenti del template. Pertanto, verrà selezionato tramite il sovraccarico dello const T&.

Di solito, si desidera mantenere la funzione di riferimento universale e accoppiarlo con std::forward<T>() per perfezionare l'argomento o gli argomenti. Ciò elimina la necessità del sovraccarico di const T&, poiché la versione di riferimento universale prenderà il sopravvento.

Si noti che solo perché si vede && in un contesto deducibile, ciò non significa che sia un riferimento universale; le && ha bisogno di essere aggiunti al tipo di essere dedotto, ecco un esempio di qualcosa che è in realtà un riferimento rvalue:

template<class T> 
void f_(std::vector<T>&& arg) // this is an rvalue reference; not universal 
{ 
    cout << "void f(T&& arg): Can modify\n"; 
} 

Ecco un gran parlare sulla questione: https://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Scott-Meyers-Universal-References-in-Cpp11

+2

@Artur Leggi anche [regole di compressione dei riferimenti] (https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers). – vsoftco

1

T&& può essere chiamato come universal/forwarding riferimento.
riferimento collasso regole:

  1. Un & & diventa un &
  2. Un & & & diventa un &
  3. Un & & & diventa un &
  4. Un & & 0.123.& diventa un & &

template<typename T> void foo(T&&);

Qui, si verifica quanto segue:

  1. Quando foo è chiamato su un lvalue di tipo A, allora T risolve in un & e quindi, in base alle regole di compressione di riferimento sopra, l'argomento tipo diventa effettivamente A &.
  2. Quando foo viene chiamato su un valore di rvalore di tipo A, T risolve in A e quindi il tipo di argomento diventa A & &.

Nel tuo caso:

template<class T> void f_(T&& arg); 
f_(block); //case 1 
f_(MemoryBlock()); //case 2 

Nel caso 1:
T = MemoryBlock & allora T & & diventa T & & & ==> dà T &
Nel caso 2:
T = MemoryBlock quindi T & & diventa T & & ==> dà T & &

Per queste entrambi i casi

template<class T> void f_(T&& arg) 

è la scelta migliore per il compilatore da qui il suo presa invece di

template<class T> 
void f_(const T& arg) 
Problemi correlati