2013-06-04 12 views
16

Si consideri il seguente codice:interpretazione di riferimento in C++

#include <iostream> 

template<typename T> 
void inc1(T&& x) 
{ 
    T y = x; 
    ++y; 
} 

template<typename T> 
void inc2(T& x) 
{ 
    T y = x; 
    ++y; 
} 

int main() 
{ 
    int a = 10; 
    inc1(a); // a is now 11 

    int b = 10; 
    inc2(b); // b remains 10 
} 

Dopo la sostituzione abbiamo

void inc1(int& x) 
{ 
    int& y = x; // reference to x 
    ++y; // increments x 
} 

void inc2(int& x) 
{ 
    int y = x; // copy of x 
    ++y; // increments the copie 
} 

In inc1, x è di tipo int& perché entrambi int& e T&& sono riferimenti, ma non entrambi sono r -valori.

Analogamente, in inc2, x è di tipo int&, perché sia ​​nuovamente int& e T& sono riferimenti, ma non entrambi sono R-valori.

La mia domanda riguarda y: perché in inc1, y è di tipo int&, mentre in inc2, y è di tipo int?

Ho osservato questo su gcc 4.8.1 e microsoft v110 e v120_ctp.

risposta

14

In entrambe le chiamate di funzione, ciò che si passa alla funzione è un int & (nel senso di: "un lvalue di tipo int"). Quindi, presentato con la dichiarazione di inc1, il compilatore deve dedurre T tale che T && corrisponda all'argomento che hai fornito, ad esempio int &. L'unico modo per fare ciò è supporre che T sia int &, perché quindi T && è int & &&, che equivale a int &. Quindi T diventa int & e il locale viene dichiarato come tale.

D'altra parte, nel inc2, il compilatore deve dedurre T tale che T & corrisponde al tipo di argomento che hai fornito, che è ancora int &. Questo è più semplice se si suppone che T sia semplicemente int, quindi questo è ciò che si ottiene per il tipo di locale quindi.


rispondere a un paio di osservazioni (che sono stati nel frattempo eliminati): Se si dispone di una funzione con un tipo di argomento predefinito, come ad esempio

poi, quando si chiama questo esempio come inc3(a), il compilatore applicherà qualsiasi conversione implicita necessaria all'argomento per adattarlo. Nel caso di inc3(a), questo significa trasformare da int & (nel senso di lvalue) a int (nel senso di rvalue) – che è chiamato conversione lvalue-rvalue e una conversione implicita valida. Fondamentalmente equivale a trasformare la variabile a nel valore che rappresenta in quel momento.

Ma quando si dichiara un modello , come ad esempio inc1 e inc2 dalla questione, e l'argomento della funzione è definita in termini di un parametro di template, quindi il compilatore non è, o non solo, cercare di applicare conversioni implicite alla discussione per renderlo adatto.Invece, sarà scegliere il parametro tipo parametro T in modo che corrisponda al tipo di argomento che hai fornito. Le regole per questo sono complicate, ma nel caso di una dichiarazione argomento di tipo T &&, funzionano come descritto sopra. (Nel caso di una pura dichiarazione argomento T, un argomento lvalue ancora sottoposti Ivalue a rvalue conversione e T sarà dedotta come int, non int &.)

Ecco perché, mentre int && è un riferimento rvalue , T && (dove T è un parametro di modello) non è necessariamente un riferimento di rvalue. Invece, è qualsiasi risultato dall'adattamento di T all'argomento fornito. L'espressione T && in questo caso è stata quindi chiamata riferimento universale (come indicato su lvalue o riferimento di valore) – è un riferimento che diventa o un valore di valore o valore di riferimento, se necessario.

+0

Perché dici passiamo un 'int &'? È ovvio? – perreal

+0

@perreal Sì. Gli argomenti passati sono variabili dichiarate come 'int':' int a = 10; 'e' int b = 10; ', rispettivamente. Quando tali variabili sono usate come argomenti per funzionare chiamate, il loro tipo è 'int &' (I.e sono riferimenti lvalue). – jogojapan

+1

Per correggere il mio commento precedente: Il tipo di queste variabili è infatti 'int &', quindi sono _valori_ (non i riferimenti lvalue. Questo termine si applica solo alle nuove variabili dichiarate come riferimenti a 'a' o' b'). – jogojapan

7

S14.8.2.1 [temp.deduct.call] dice:

Template detrazione argomento è fatta confrontando ogni funzione tipo di parametro template (lo chiamano P) con il tipo del corrispondente argomento della la chiamata (chiamarla A) come descritto di seguito.

Quindi, stiamo cercando di elaborare un P dato A di tipo int.

S14.8.2.3 continua:

Se P è un tipo cv-qualificato, il livello superiore CV-qualificazioni di tipo P vengono ignorati per il tipo di detrazione. Se P è un tipo di riferimento , il tipo indicato da P viene utilizzato per la deduzione del tipo. Se P è un riferimento di rvalore a un parametro di modello cv-non qualificato e l'argomento è un valore di l, il tipo "riferimento di lvalue ad A" viene utilizzato al posto di A per la deduzione di tipo. [Esempio:

template <class T> int f(T&&);   // <--- YOUR TEMPLATE IS LIKE THIS 
template <class T> int g(const T&&); 
int i; 
int n1 = f(i); // calls f<int&>(int&) // <--- YOUR CALL IS LIKE THIS 
int n2 = f(0); // calls f<int>(int&&) 
int n3 = g(i); // error: would call g<int>(const int&&), which 
       // would bind an rvalue reference to an lvalue 

-end esempio]

La chiamata è come f(i) nell'esempio - che crea un'istanza di un funzione della forma f<int&>(int&) ... cioè T è int& , che è il motivo per cui T y = x crea un riferimento a x.

Vedere anche pagina di Scott Meyers a http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers