Sta catturando un oggetto di nuova costruzione con un comportamento indefinito const ref


11

Il seguente (esempio inventato) va bene o è un comportamento indefinito:

// undefined behavior?
const auto& c = SomeClass{};

// use c in code later
const auto& v = c.GetSomeVariable();

Risposte:


12

È sicuro. Const ref prolunga la durata del temporaneo. Lo scopo sarà quello di const ref.

La durata di un oggetto temporaneo può essere estesa legandosi a un riferimento al valore costante o ad un riferimento al valore (dal C ++ 11), vedere l' inizializzazione del riferimento per i dettagli.

Ogni volta che un riferimento è associato a un temporaneo o ad un suo oggetto secondario, la durata del temporaneo viene estesa in modo da corrispondere alla durata del riferimento, con le seguenti eccezioni :

  • un limite temporaneo a un valore di ritorno di una funzione in un'istruzione return non viene esteso: viene distrutto immediatamente alla fine dell'espressione di ritorno. Tale funzione restituisce sempre un riferimento penzolante.
  • un limite temporaneo a un membro di riferimento in un elenco di inizializzatori del costruttore persiste solo fino alla chiusura del costruttore, non finché l'oggetto esiste. (nota: tale inizializzazione è mal formata dal DR 1696).
  • esiste un limite temporaneo a un parametro di riferimento in una chiamata di funzione fino alla fine dell'espressione completa contenente quella chiamata di funzione: se la funzione restituisce un riferimento, che sopravvive all'espressione completa, diventa un riferimento pendente.
  • esiste un limite temporaneo a un riferimento nell'inizializzatore utilizzato in una nuova espressione fino alla fine dell'espressione completa contenente quella nuova espressione, non finché l'oggetto inizializzato. Se l'oggetto inizializzato sopravvive all'espressione completa, il suo membro di riferimento diventa un riferimento pendente.
  • esiste un limite temporaneo a un riferimento in un elemento di riferimento di un aggregato inizializzato utilizzando la sintassi di inizializzazione diretta (parentesi) rispetto alla sintassi di inizializzazione di elenco (parentesi graffe) fino alla fine dell'espressione completa contenente l'inizializzatore. struct A { int&& r; }; A a1{7}; // OK, lifetime is extended A a2(7); // well-formed, but dangling reference

In generale, la durata di un temporaneo non può essere ulteriormente estesa "passandolo": un secondo riferimento, inizializzato dal riferimento a cui era limitato il temporaneo, non influisce sulla sua durata.

come ha sottolineato @Konrad Rudolph (e vedi l'ultimo paragrafo sopra):

"Se c.GetSomeVariable()restituisce un riferimento a un oggetto locale o un riferimento che sta prolungando la durata di alcuni oggetti, l'estensione di durata non si avvia"


1
Dovresti citare la fonte di quella citazione.
Razze di leggerezza in orbita

@LightnessRaceswithMonica done. Stavo cercando un testo migliore.
Oblivion

2
Sarebbe bene sottolineare che questo è vero solo per i valori . Se c.GetSomeVariable()restituisce un riferimento a un oggetto locale o un riferimento che sta prolungando la durata di alcuni oggetti, l'estensione di durata non si avvia.
Konrad Rudolph

@KonradRudolph Grazie! Ho aggiunto anche l'eccezione.
Oblivion

4

Non ci dovrebbero essere problemi qui, grazie all'estensione a vita . L'oggetto di nuova costruzione sopravviverà fino a quando il riferimento non esce dal campo di applicazione.


3

Sì, questo è perfettamente sicuro: l'associazione a un constriferimento estende la durata del temporaneo all'ambito di tale riferimento.

Si noti che il comportamento non è però transitivo . Ad esempio, con

const auto& cc = []{
    const auto& c = SomeClass{};
    return c;
}();

cc Dangles.


2

Questo è sicuro

[class.temporary]/5: Esistono tre contesti in cui i provvisori vengono distrutti in un punto diverso rispetto alla fine dell'espressione completa . [..]

[class.temporary]/6: Il terzo contesto è quando un riferimento è associato a un oggetto temporaneo. L'oggetto temporaneo a cui è associato il riferimento o l'oggetto temporaneo che è l'oggetto completo di un oggetto secondario a cui è associato il riferimento persiste per tutta la durata del riferimento se il valore a cui è associato il riferimento è stato ottenuto attraverso uno dei seguenti : [molte cose qui]


1

È sicuro in questo caso specifico. Si noti tuttavia che non tutti i provvisori sono sicuri da acquisire per riferimento const ... per esempio

#include <stdio.h>

struct Foo {
    int member;

    Foo() : member(0) {
        printf("Constructor\n");
    }

    ~Foo() {
        printf("Destructor\n");
    }

    const Foo& method() const {
        return *this;
    }
};

int main() {
    {
        const Foo& x = Foo{};        // safe
        printf("here!\n");
    }
    {
        const int& y = Foo{}.member; // safe too (special rule for this)
        printf("here (2)!\n");
    }
    {
        const Foo& z = Foo{}.method(); // NOT safe
        printf("here (3)!\n");
    }
    return 0;
}

Il riferimento ottenuto per zNON è sicuro da usare perché l'istanza temporanea verrà distrutta alla fine dell'espressione completa, prima di raggiungere l' printfistruzione. L'output è:

Constructor
here!
Destructor
Constructor
here (2)!
Destructor
Constructor
Destructor
here (3)!
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.