2015-01-26 7 views
13

Ci sono casi in cui voglio un riferimento a un oggetto ma invece ottengo una copia. Ecco un esempio:Quando e perché ottengo una copia durante l'inizializzazione di un riferimento?

std::pair<const std::string, int> foo("hello", 5); 
    const std::pair<std::string, int> & bar = foo; 

    std::cout << "foo: " << foo.first << " " << foo.second << std::endl; 
    std::cout << "bar: " << bar.first << " " << bar.second << std::endl; 
    foo.second = 7; 
    std::cout << "foo: " << foo.first << " " << foo.second << std::endl; 
    std::cout << "bar: " << bar.first << " " << bar.second << std::endl; 

Questo produce:

foo: hello 5 
bar: hello 5 
foo: hello 7 
bar: hello 5 

Quindi, apparentemente una copia di foo è stato realizzato, mentre la sintassi suggerisce (almeno per me) che il programmatore ha voluto un riferimento ad esso . Questo viola il principio che un riferimento dovrebbe essere un alias per qualcosa. Sarebbe bello se qualcuno potesse spiegare cosa sta succedendo e perché.

(Nota: Mi sono imbattuto in questo here)

+3

I tipi sono diversi. – Borgleader

risposta

15

I tipi di fondo della foo e bar sono diverse, così una temporanea viene creato utilizzando una conversione implicita dal tipo sul RHS a quello sul lato sinistro *. Lo standard C++ consente un riferimento a const per associare a un temporaneo ed estenderne la durata.

Il const riferimento bar si lega a tale temporaneo, che è un oggetto distinto da foo.

Se si sceglie di usare gli stessi tipi, si otterrebbe il risultato che ci si aspetta:

std::pair<const std::string, int> foo("hello", 5); 
const std::pair<const std::string, int> & bar = foo; 

o

std::pair<std::string, int> foo("hello", 5); 
const std::pair<std::string, int> & bar = foo; 

sarebbe cedere

foo: hello 5 
bar: hello 5 
foo: hello 7 
bar: hello 7 

* std::pair ha un template constructor che consente questa conversione implicita da un tipo di coppia a un altro.

+1

E in quali casi l'assegnazione di tipi incompatibili crea un valore temporaneo rispetto al solo lancio di un errore del compilatore? – Sarien

+0

@Sarien Ci deve essere una conversione valida tra 'T1' e' T2' in 'const T2 & = T1'; – juanchopanza

+0

@Sarien il compilatore cerca una conversione valida. Può invocare il costruttore di 'pair' con copie del valore nella coppia originale, quindi lo farà. –

5

Questa è una proprietà speciale di riferimenti a const (e di referral rvalue, naturalmente). Questi riferimenti possono legarsi a oggetti temporanei.

noti che std::pair<const std::string, int> (il tipo di foo) è un tipo diverso std::pair<std::string, int> (del tipo a cui bar vuole riferirsi, modulo const). Non è presente alcun oggetto di tipo std::pair<std::string, int> nel codice, pertanto bar non può essere associato a nessun oggetto di questo tipo.

Tuttavia, come ho già detto, i riferimenti a const e ai riferimenti di rvalue possono essere associati ai provvisori. E un oggetto di tipo std::pair<std::string, int> può essere creato in modo implicito da un oggetto di tipo std::pair<const std::string, int>. Pertanto, viene creato un oggetto temporaneo di questo tipo e bar è associato a tale temporaneo. Questo legame di riferimento estende inoltre la durata del periodo temporaneo a quello di bar*.

Ecco perché ne ottieni una copia. Se è stato modificato il tipo di bar in std::pair<std::string, int> & (vale a dire caduto il const), si otterrebbe invece un errore di compilazione che un riferimento di lvalue non const non può associare a un temporaneo.


* Nel caso particolare di una variabile di tipo riferimento legato ad un temporaneo, durata della temporanea estenderà solo fino alla fine del costruttore che inizializzato il riferimento.

Problemi correlati