errore: inizializzazione non valida del riferimento non const di tipo "int &" da un valore di tipo "int"


87

Forma sbagliata:

int &z = 12;

Forma corretta:

int y;
int &r = y;

Domanda :
perché il primo codice è sbagliato? Qual è il " significato " dell'errore nel titolo?


4
I temporanei non possono essere associati a riferimenti non costanti. int (12) è temporaneo in questo caso.
Prasoon Saurav

@PrasoonSaurav Cosa intendi per 12 temporanei? Mancanza di concetti qui (da parte mia :))
Aquarius_Girl

2
Nota che non c'è una ragione tecnica rigorosa per questa restrizione. Sarebbe stato altrettanto facile da implementare consentire riferimenti mutevoli ai provvisori. Proibirlo è una decisione progettuale del C ++, poiché una tale costruzione sarebbe una progettazione scadente con un rischio molto maggiore di essere inavvertitamente abusato rispetto all'utilità autentica. (Solo una volta ho riscontrato un bisogno artificioso di una cosa del genere.)
Kerrek SB

@KerrekSB la necessità più comune per un riferimento vincolante a un oggetto rvalue probabilmente è(ostringstream() << "x=" << x).str()
curioso

@curiousguy: Sì, questo è il contenuto del collegamento che ho pubblicato.
Kerrek SB

Risposte:


132

C ++ 03 3.10 / 1 dice: "Ogni espressione è un lvalue o un rvalue." È importante ricordare che la stima contro la stima è una proprietà delle espressioni, non degli oggetti.

Lvalues ​​denomina oggetti che persistono oltre una singola espressione. Ad esempio, obj, *ptr, ptr[index], e ++xsono tutti lvalue.

I valori sono provvisori che evaporano alla fine della piena espressione in cui vivono ("al punto e virgola"). Ad esempio, 1729, x + y, std::string("meow"), e x++sono tutti rvalues.

L'operatore address-of richiede che il suo "operando sia un lvalue". se potessimo prendere l'indirizzo di un'espressione, l'espressione è un lvalue, altrimenti è un rvalue.

 &obj; //  valid
 &12;  //invalid

2
" se potessimo prendere l'indirizzo di un'espressione, l'espressione è un lvalue, altrimenti è un rvalue. " se solo il C ++ fosse così semplice! (ma la sfumatura non è realmente rilevante qui) "I valori sono provvisori" temporanei cosa? oggetti?
curioso

2
@curiousguyRvalues ​​sono provvisori che scompaiono alla fine della piena espressione in cui vivono. Per rispondere semplicemente al motivo per cui l'espresso int & = 12;non è valido, lo standard dice che una stringa letterale è un lvalue, altri letterali sono rvalue.
BruceAdi

@curiousguy: Sì. I rvalues ​​sono oggetti temporanei , ma non tutti i rvalues ​​sono oggetti temporanei; alcuni non sono nemmeno oggetti.
Nawaz

" Ad esempio, 1729, x + y, std :: string (" meow ") e x ++ sono tutti rvalues. " Ma std::string("meow")costruisce un oggetto di tipo std::stringe restituisce un rvalue che designa questo oggetto, 1729non ha effetti collaterali e restituisce un valore 1729 come valore di tipo int.
curioso

1
@curiousguy: l'affermazione "Lvalues name objects that persist beyond a single expression."è corretta al 100%. Al contrario, il tuo esempio (const int &)1non è corretto, perché NON è un oggetto "con nome".
Nawaz

53
int &z = 12;

Sul lato destro, un oggetto temporaneo di tipo intviene creato dall'integrale letterale 12, ma il temporaneo non può essere associato a un riferimento non const. Da qui l'errore. È lo stesso di:

int &z = int(12); //still same error

Perché viene creato un temporaneo? Poiché un riferimento deve fare riferimento a un oggetto nella memoria e affinché un oggetto esista, deve essere prima creato. Poiché l'oggetto è senza nome, è un oggetto temporaneo . Non ha nome. Da questa spiegazione, è diventato abbastanza chiaro perché il secondo caso va bene.

Un oggetto temporaneo può essere associato al riferimento const, il che significa che puoi farlo:

const int &z = 12; //ok

Riferimento C ++ 11 e Rvalue:

Per ragioni di completezza, vorrei aggiungere che C ++ 11 ha introdotto rvalue-reference, che può legarsi a un oggetto temporaneo. Quindi in C ++ 11, puoi scrivere questo:

int && z = 12; //C+11 only 

Nota che c'è &&intead di &. Si noti inoltre che constnon è più necessario, anche se l'oggetto a cui si zlega è un oggetto temporaneo creato dall'integrale letterale 12.

Poiché C ++ 11 ha introdotto rvalue-reference , int&è ora chiamato lvalue-reference .


10

12è una costante del tempo di compilazione che non può essere modificata a differenza dei dati a cui fa riferimento int&. Quello che puoi fare è

const int& z = 12;

2
@curiousguy, sii coerente, hai detto "Devi capire che queste sono regole C ++. Esistono e non hanno bisogno di alcuna giustificazione" (per non parlare della prima edizione). Come posso interpretare il tuo lamentarti di non dare ciò che secondo te non è necessario?
Michael Krelin - hacker

2
@ MichaelKrelin-hacker: Tecnicamente no, non puoi (mai) associare un riferimento a un valore (o compilare una costante di tempo), lo standard è abbastanza esplicito su ciò che effettivamente accade: Altrimenti, viene creato un temporaneo di tipo "cv1 T1" e inizializzato dall'espressione dell'inizializzatore utilizzando le regole per un'inizializzazione della copia senza riferimento (8.5). Il riferimento è quindi vincolato al temporaneo Cioè la sintassi è consentita, ma la semantica non è quella di legare un riferimento alla costante, ma piuttosto di legarlo al temporaneo che viene implicitamente creato.
David Rodríguez - dribeas

1
@curiousguy: Le regole del linguaggio fanno parte del design e, il più delle volte, ci sono giustificazioni sul motivo per cui il linguaggio è stato progettato in questo modo. In questo caso particolare, come in C non ti è permesso prendere l'indirizzo di un valore (non ne ha uno), né puoi associare un riferimento. Consideriamo ora una funzione void f( vector<int> const & ), idiomatica per passare un vettore che non deve essere modificato. Il problema ora è che non f( vector<int>(5) )sarebbe corretto e l'utente dovrebbe fornire un sovraccarico diverso, il void f( vector<int> v ) { f(v); }che è banale.
David Rodríguez - dribeas

1
... ora poiché ciò sarebbe doloroso per gli utenti del linguaggio, i progettisti hanno deciso che il compilatore avrebbe eseguito l'operazione equivalente per te, nella chiamata f( vector<int>(5) ), il compilatore crea un temporaneo e quindi lega il riferimento a quello temporaneo, e allo stesso modo se c'era una conversione implicita da 5direttamente. Ciò consente al compilatore di generare una singola firma per la funzione e consente l'implementazione della funzione da parte di un singolo utente. Da lì in poi, un comportamento simile viene definito per il resto degli usi di riferimenti costanti per coerenza.
David Rodríguez - dribeas

1
@ MichaelKrelin-hacker: i riferimenti sono alias di oggetti e un valore non è un oggetto. A seconda del contesto, i riferimenti possono essere solo alias, il compilatore rimuove il riferimento e usa semplicemente l'identificatore per indicare qualunque cosa significasse l'oggetto originale ( T const & r = *ptr;qualsiasi uso successivo di rnella funzione può essere sostituito da *ptr, e rnon deve esistere in fase di esecuzione) oppure potrebbe dover essere implementato mantenendo l'indirizzo dell'oggetto che alias (considera di memorizzare un riferimento come membro di un oggetto) - che è implementato come un puntatore con riferimento automatico.
David Rodríguez - dribeas

4

Le associazioni di riferimento non const e const seguono regole diverse

Queste sono le regole del linguaggio C ++:

  • un'espressione composta da un numero letterale ( 12) è un "rvalue"
  • non è consentito creare un riferimento non const con un rvalue: int &ri = 12;è mal formato
  • è consentito creare un riferimento const con un rvalue: in questo caso, un oggetto senza nome viene creato dal compilatore; questo oggetto persisterà fintanto che il riferimento stesso esiste.

Devi capire che queste sono regole C ++. Lo sono e basta.

È facile inventare un linguaggio diverso, diciamo C ++ ', con regole leggermente diverse. In C ++ ', sarebbe consentito creare un riferimento non const con un rvalue. Non c'è niente di incoerente o impossibile qui.

Ma consentirebbe un codice rischioso in cui il programmatore potrebbe non ottenere ciò che intendeva, ei progettisti C ++ hanno giustamente deciso di evitare quel rischio.


0

I riferimenti sono "puntatori nascosti" (non nulli) a cose che possono cambiare (lvalues). Non puoi definirli come una costante. Dovrebbe essere una cosa "variabile".

MODIFICARE::

Sto pensando a

int &x = y;

quasi equivalente a

int* __px = &y;
#define x (*__px)

dove __pxè un nuovo nome, e #define xfunziona solo all'interno del blocco contenente la dichiarazione di xriferimento.


1
Perché puoi, se il riferimento è const:)
Michael Krelin - hacker

Ma l'esempio del poster non eraconst
Basile Starynkevitch,

Sì, intendevo la tua formulazione "i riferimenti sono puntatori a cose che possono cambiare" - si tratta di riferimenti senza costi.
Michael Krelin - hacker

"I riferimenti sono" puntatori nascosti " " sbagliati " a cose che possono cambiare cose " sbagliate " che possono cambiare (lvalues). "
Wrong

@Loki: potresti spiegare di più?
Basile Starynkevitch
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.