2016-02-10 8 views
5

Primo esempioPerché questi due esempi di riferimenti rvalue hanno un comportamento diverso?

int a = 0; 
auto && b = ++a; 
++a; 
cout << a << b << endl; 

stampe 22

secondo esempio

int a = 0; 
auto && b = a++; 
++a; 
cout << a << b << endl; 

stampe 20

Domanda: Perché nel primo esempio ++a nella 3a riga si incrementa anche e perché non esiste un simile comportamento nel secondo esempio?

Aggiornamento:New question sorto.

+0

Ti darò un suggerimento - "a" non è un valore. – erip

+0

@erip Sì, lo so, ma quale è la differenza in due esempi per 'b'? – vladon

+0

Nel secondo esempio, 'b' ottiene il valore di' a' senza incrementarlo prima, come nel primo esempio. – x13

risposta

10

Poiché pre-incremento (++a) incrementa il valore prima del a, memorizza il risultato, e quindi restituisce il riferimento a. Ora a e b puntano effettivamente allo stesso oggetto.

post-incremento (a++), tuttavia, prima memorizza il valore corrente di a in una temporanea, incrementa a, e restituisce questo temporanei - a cui il vostro punti ref rvalue. a e b puntare a oggetti diversi, in particolare: b è un valore temporaneo contenente il valore di a prima dell'incremento.

Questo è il motivo per cui è incoraggiato a utilizzare ++it sopra it++ per iteratori e altri oggetti complessi che definiscono incremento/decremento: quest'ultima crea una copia temporanea e quindi può essere più lento.

+0

È UB usare 'b' dopo' a ++ 'nel secondo esempio? – vladon

+0

E perché nel primo caso non c'è temporaneo? Perché "auto && b = a ++" non è equivalente a "auto && b = a; a ++ '? – vladon

+0

@vladon: I _think_ che il temporaneo vivrà fino a quando il riferimento ad esso non rientra nell'ambito, il che significa che non è UB, ma non ne sono sicuro. –

1

Nel secondo caso (post-incremento) b fa effettivamente riferimento al temporaneo creato per (a ++), quindi gli incrementi non influiscono su b.

2

La differenza è che ++a è un lvalue, tuttavia non lo è a++. Questo è specificato da C++ 14 [expr.pre.incr]/1:

L'operando prefix ++ è modificato aggiungendo 1 [...] La operando è un valore assegnabile modificabili. [...] Il risultato è l'operando aggiornato; si tratta di un lvalue

e [expr.post.incr]/1:

[...] Il risultato è un prvalue.


Ora consideriamo auto && b = ++a;. ++a è un lvalue. auto&& è un riferimento di inoltro. I riferimenti di inoltro possono effettivamente associarsi a lvalue: lo auto può dedurre esso stesso un tipo di riferimento. Questo codice si riduce a int &b = ++a;.

Quando un riferimento è associato a un lvalue dello stesso tipo, il riferimento si collega direttamente, quindi b diventa un altro nome per a.


Nel secondo esempio, auto && b = a++;, a++ è un prvalue. Ciò significa che non ha un indirizzo associato e non ha più alcuna relazione con la variabile a. Questa riga ha lo stesso comportamento di ++a; auto && b = (a + 0);.

In primo luogo, poiché a++ è un valore di prua, auto&& viene dedotto a int&&. (Ad esempio, auto deduce a int). Quando un riferimento di tipo non di classe è associato a un valore di provalutazione, un oggetto temporaneo viene inizializzato dalla copia dal valore. Questo oggetto ha una durata estesa per corrispondere al riferimento.

Così b nel secondo caso si associa a un oggetto diverso da a, un int "temporaneo" (che non è così temporaneo, poiché dura finché b fa).

Le regole di collegamento di riferimento sono in [dcl.init.ref].

Problemi correlati