2012-07-31 13 views
5
struct A { 
    A(int) : i(new int(783)) { 
     std::cout << "a ctor" << std::endl; 
    } 

    A(const A& other) : i(new int(*(other.i))) { 
     std::cout << "a copy ctor" << std::endl; 
    } 

    ~A() { 
     std::cout << "a dtor" << std::endl; 
     delete i; 
    } 

    void get() { 
     std::cout << *i << std::endl; 
    } 

private: 
    int* i; 
}; 

const A& foo() { 
    return A(32); 
} 

const A& foo_2() { 
    return 6; 
} 

int main() 
{ 
    A a = foo(); 
    a.get(); 
} 

Lo so, restituire i riferimenti ai valori locali è negativo. D'altro canto, il riferimento const dovrebbe estendere la durata di un oggetto temporaneo.Cosa succede esattamente quando si restituisce un riferimento const a un oggetto locale?

Questo codice produce un'uscita UB. Quindi nessuna estensione della vita.

Perché? Voglio dire qualcuno può spiegare cosa sta succedendo passo dopo passo?

Dov'è l'errore nella mia catena di ragionamento?

foo():

  1. A (32) - ctor

  2. ritorno A (32) - viene creato un riferimento const all'oggetto locale e viene restituito

  3. A bis = pippo(); - a è inizializzato da foo() valore restituito, il valore restituito esce dall'ambito (fuori dall'espressione) e viene distrutto, ma a è già inizializzato;

(Ma in realtà distruttore viene chiamato prima copia costruttore)

foo_2():

  1. ritorno 6 - Temp oggetto di tipo A è stato creato in modo implicito, un riferimento const a questo oggetto viene creato (estendendo la sua vita) e viene restituito

  2. A a = foo(); - a è inizializzato da foo() valore restituito, il valore restituito esce dall'ambito (fuori dall'espressione) e viene distrutto, ma a è già inizializzato;

(ma in realtà distruttore viene chiamato prima costruttore di copia)

+1

"il riferimento const dovrebbe estendere la durata di un oggetto temporaneo" <- erm, no quando si tratta di oggetti-lifetime i riferimenti non-const e const sono uguali e fanno * not * estenderlo. – Giel

+2

Penso che questo sia ciò di cui Alexander sta parlando in termini di prolungamento della durata: http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/ – vmpstr

+1

@Giel: No. Il riferimento Const ** può ** estendere la durata dell'oggetto temporaneo. I riferimenti constst e non const sono molto diversi quando si tratta di lavorare con i temporaries. In questo caso funziona solo diversamente da ciò che l'OP sembra aspettarsi. – AnT

risposta

11

Regolamento di durata di vita temporaneo per ogni specifico contesto sono esplicitamente enunciati nella specifica del linguaggio. E si dice che

12.2 oggetti temporanei

5 Il secondo contesto è quando un riferimento è associato a un temporaneo. [...] Un limite temporaneo al valore restituito in una dichiarazione di ritorno funzione (6.6.3) persiste finché la funzione non viene chiusa. [...]

L'oggetto temporaneo viene distrutto al momento dell'uscita dalla funzione. Ciò accade prima che inizi l'inizializzazione dell'oggetto destinatario.

Sembra che tu pensi che il tuo temporaneo debba in qualche modo vivere più a lungo di quello. Apparentemente stai provando ad applicare la regola che dice che il temporaneo dovrebbe sopravvivere fino alla fine della piena espressione. Ma questa regola non si applica ai provvisori creati all'interno delle funzioni. Le vite di questi temporari sono governate da regole proprie e dedicate.

Sia il vostro foo sia il vostro foo_2 producono un comportamento non definito, se qualcuno tenta di utilizzare il riferimento restituito.

+0

Ma se "un vincolo temporaneo al valore restituito in una funzione return statement (6.6.3) persiste finché la funzione non esce" il valore temporaneo shoudnt viene distrutto prima dell'inizializzazione in A foo_3() {return A (54);}? – Alexander

+1

@Alexander - No, 'A foo_3()' restituisce una copia del valore. Il valore copiato non viene distrutto alla fine della funzione. Quando si restituisce un riferimento, il riferimento è ancora lì - semplicemente non si riferisce più a nulla. –

+0

Vedo .. Ma allora il copistore dovrebbe essere chiamato 2 volte nell'espressione A a = foo_3(); 1a volta quando il valore di ritorno viene copiato da locale temporaneo e 2a volta quando si inizializza A a. Ma è chiamato solo una volta. O è solo un'ottimizzazione? – Alexander

3

Si sta erroneamente interpretando "fino all'uscita dalla funzione". Se davvero si vuole utilizzare un riferimento const per prolungare la durata di un oggetto al di là foo, utilizzare

A foo() { 
    return A(32); 
} 
int main() { 
    const A& a = foo(); 
} 

Devi tornare dal fooper valore, e quindi utilizzare un riferimento const per fare riferimento al valore di ritorno, se desideri estendere le cose nel modo in cui ti aspetti.

Come ha detto @AndreyT, l'oggetto viene distrutto nella funzione che ha il const &. Volete che il vostro oggetto di sopravvivere al di là foo, e, quindi, si dovrebbe non avere const & (o &) in qualsiasi punto foo o il tipo di ritorno di foo. La prima menzione di const & dovrebbe essere in main, poiché questa è la funzione che dovrebbe mantenere vivo l'oggetto.

Si potrebbe pensare che questo codice di ritorno per valore sia lento poiché sembra che ci siano copie di A fatte nel reso, ma questo non è corretto. Nella maggior parte dei casi, il compilatore può costruire A una sola volta, nella sua posizione finale (cioè nello stack della funzione di chiamata), e quindi impostare il riferimento pertinente.

Problemi correlati