2014-04-03 6 views
11

Non possiamo scrivere int& ref = 40 perché abbiamo bisogno di lvalue sul lato destro. Ma possiamo scrivere const int& ref = 40. Perché è possibile? 40 è rvalue invece lvalueConst reference e lvalue

So che questa è un'eccezione, ma perché?

+0

Definitivamente un dupe. –

risposta

10

Come Stroustrup dice:

L'inizializzatore di un const T & Non occorre essere un lvalue o anche di tipo T. In tal caso:

[1] Primo, conversione di tipo implicita T viene applicato se necessario.

[2] Quindi, il valore risultante viene posto in una variabile temporanea di tipo T.

[3] Infine, questa variabile temporanea viene utilizzato come valore del inizializzatore.

Quindi, quando si digita const int& ref = 40, la variabile temporanea int viene creata dietro le quinte e il riferimento è associato a questa variabile temporanea.

11

C'è una regola nella lingua che consente di associare un riferimento di constval a un valore. La ragione principale di questa regola è che, se non era presente, allora si dovrebbe fornire diversi sovraccarichi di funzioni per essere in grado di utilizzare provvisori come argomenti:

class T; // defined somewhere 
T f(); 
void g(T const &x); 

Con questa regola nel luogo che si può fare g(f()) , senza di essa, per essere in grado di farlo si dovrà creare una diversa g overload che accetta un rvalue (e questo è da un tempo in cui rvalue riferimenti non erano nemmeno nella lingua!)

+0

Non sono ancora sicuro del motivo per cui [la durata è estesa] (http://stackoverflow.com/q/20546981/596781), però :-( –

+0

@KerrekSB: Immagino che la ragione originale fosse che in questo modo potevi scrivere 'T const & _ = ...;' o 'T const _ = ...; 'indifferentemente ed entrambi funzionerebbero, il primo è più efficiente quando l'espressione produce un riferimento (nessuna copia). E 'davvero sfortunato, però, perché lascia entrare molti ma. –

+1

@KerrekSB: Pensavo di averlo indirizzato nella domanda collegata. Questo è un effetto collaterale di consentire l'associazione nel contesto generale (non solo negli argomenti della funzione) e non volendo che causi un comportamento indefinito ad ogni uso. Senza l'estensione della durata: 'T const & x = f(); 'seguito da * qualsiasi * odr-uso di' x' sarebbe un comportamento indefinito, sollevando la questione del perché permetterlo in primo luogo? Quindi o fornite consistenza (riferimenti di riferimento in * qualsiasi * contesto) con estensione a vita, oppure accettate incoerenze e non vi è alcuna necessità di estensione a vita. Il linguaggio ha fatto il primo. –

2

perché è possibile?

40 è un letterale qui. I riferimenti costanti possono essere inizializzati con letterali e temporanei per prolungare la loro vita. Questo può essere fatto in questo modo da un compilatore:

int const& ans = 40; 
// transformed: 
int __internal_unique_name = 40; 
int const& ans = __internal_unique_name; 

Un'altra situazione è quando si dispone di una funzione, ad esempio:

void f(std::string const& s); 

e si desidera chiamare con

f("something"); 

Questo la variabile temporanea può essere solo associata al riferimento const.

+1

Questo è anche il caso con riferimenti non const ... –

+0

@ DavidRodríguez-dribeas non è la seconda situazione fornita – 4pie0

+0

Questo è esattamente ciò che il compilatore farà per te, esegue il mapping della chiamata a: 'std :: string __tmp (" qualcosa"); f (__ tmp); '. Cioè * perché è possibile * non è un motivo per consentire questo per i riferimenti 'const' (* alone *), come dimostrato dal fatto che sia Solaris CC che VS hanno supportato i riferimenti non-const vincolanti ai provvisori facendo esattamente lo stesso cosa. –

1

È possibile associare un valore rval a un riferimento const. Il linguaggio garantisce che l'oggetto associato sopravvive fino a quando non termina lo scopo del riferimento e anche in modo statico chiama il distruttore corretto. Questo è ad es. utilizzato in un'implementazione ScopeGuard (http://www.drdobbs.com/cpp/generic-change-the-way-you-write-excepti/184403758?pgno=2) per avere un comportamento simile a un distruttore virtuale senza pagare una chiamata al metodo virtuale.

+0

Il compilatore non ha una scelta che contenga. Le regole del linguaggio dettano le vite. –