Capture un objet nouvellement construit par un comportement non défini const ref


11

Est-ce que l'exemple suivant (artificiel) est correct ou est-ce un comportement non défini:

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

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

Réponses:


12

C'est sûr. Const ref prolonge la durée de vie des temporaires. La portée sera la portée de la référence const.

La durée de vie d'un objet temporaire peut être étendue en se liant à une référence const lvalue ou à une référence rvalue (depuis C ++ 11), voir l' initialisation de la référence pour plus de détails.

Chaque fois qu'une référence est liée à un temporaire ou à un de ses sous-objets, la durée de vie du temporaire est étendue pour correspondre à la durée de vie de la référence, avec les exceptions suivantes :

  • une liaison temporaire à une valeur de retour d'une fonction dans une instruction de retour n'est pas étendue: elle est détruite immédiatement à la fin de l'expression de retour. Une telle fonction renvoie toujours une référence pendante.
  • un lien temporaire à un membre de référence dans une liste d'initialisation de constructeur persiste uniquement jusqu'à ce que le constructeur se termine, pas tant que l'objet existe. (note: une telle initialisation est mal formée à partir de DR 1696).
  • une liaison temporaire à un paramètre de référence dans un appel de fonction existe jusqu'à la fin de l'expression complète contenant cet appel de fonction: si la fonction renvoie une référence, qui survit à l'expression complète, elle devient une référence pendante.
  • une liaison temporaire à une référence dans l'initialiseur utilisé dans une nouvelle expression existe jusqu'à la fin de l'expression complète contenant cette nouvelle expression, pas aussi longtemps que l'objet initialisé. Si l'objet initialisé survit à l'expression complète, son membre de référence devient une référence pendante.
  • une liaison temporaire à une référence dans un élément de référence d'un agrégat initialisé à l'aide de la syntaxe d'initialisation directe (parenthèses) par opposition à la syntaxe d'initialisation de liste (accolades) existe jusqu'à la fin de l'expression complète contenant l'initialiseur. struct A { int&& r; }; A a1{7}; // OK, lifetime is extended A a2(7); // well-formed, but dangling reference

En général, la durée de vie d'un temporaire ne peut pas être prolongée davantage en la "transmettant": une deuxième référence, initialisée à partir de la référence à laquelle le temporaire était lié, n'affecte pas sa durée de vie.

comme @Konrad Rudolph l'a souligné (et voir le dernier paragraphe ci-dessus):

"Si c.GetSomeVariable()renvoie une référence à un objet local ou une référence indiquant qu'il prolonge lui-même la durée de vie d'un objet, l'extension à vie ne se déclenche pas"


1
Vous devez citer la source de cette citation.
Courses de légèreté en orbite

@LightnessRaceswithMonica fait. Je cherchais un meilleur texte.
Oblivion

2
Il serait bon de souligner que cela n'est vrai que pour les valeurs . Si c.GetSomeVariable()renvoie une référence à un objet local ou une référence indiquant qu'il prolonge lui-même la durée de vie d'un objet, l'extension de durée de vie ne démarre pas .
Konrad Rudolph

@KonradRudolph Merci! J'ai également ajouté l'exception.
Oblivion

4

Il ne devrait pas y avoir de problème ici, grâce à l' extension de la durée de vie . L'objet nouvellement construit survivra jusqu'à ce que la référence soit hors de portée.


3

Oui, cela est parfaitement sûr: la liaison à un const référence étend la durée de vie du temporaire à la portée de cette référence.

Notez cependant que le comportement n'est pas transitif . Par exemple, avec

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

cc se balance.


2

C'est sûr.

[class.temporary]/5: Il existe trois contextes dans lesquels les temporels sont détruits à un point différent de la fin de la pleine expression . [..]

[class.temporary]/6: Le troisième contexte est lorsqu'une référence est liée à un objet temporaire. L'objet temporaire auquel la référence est liée ou l'objet temporaire qui est l'objet complet d'un sous-objet auquel la référence est liée persiste pendant toute la durée de vie de la référence si la valeur GL à laquelle la référence est liée a été obtenue par l'un des éléments suivants : [beaucoup de choses ici]


1

Il est sûr dans ce cas spécifique. Notez cependant que tous les temporaires ne sont pas sécurisés à capturer par référence const ... par exemple

#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;
}

La référence obtenue pour zn'est PAS sûre à utiliser car l'instance temporaire sera détruite à la fin de l'expression complète, avant d'atteindre l' printfinstruction. La sortie est:

Constructor
here!
Destructor
Constructor
here (2)!
Destructor
Constructor
Destructor
here (3)!
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.