web-dev-qa-db-fra.com

Un membre du groupe de référence const prolonge-t-il la vie d’un temporaire

Pourquoi cela:

#include <string>
#include <iostream>
using namespace std;

class Sandbox
{
public:
    Sandbox(const string& n) : member(n) {}
    const string& member;
};

int main()
{
    Sandbox sandbox(string("four"));
    cout << "The answer is: " << sandbox.member << endl;
    return 0;
}

Donne la sortie de:

La réponse est:

Au lieu de:

La réponse est: quatre

148
Kyle

Seules les références local const prolongent la durée de vie.

La norme spécifie ce comportement dans le § 8.5.3/5, [dcl.init.ref], la section sur les initialiseurs de déclarations de référence. La référence dans votre exemple est liée à l'argument du constructeur n et devient invalide lorsque l'objet n est lié à sortir de la portée.

L'extension de durée de vie n'est pas transitive via un argument de fonction. §12.2/5 [class.temporary]:

Le second contexte est celui où une référence est liée à un temporaire. Le temporaire auquel la référence est liée ou le temporaire qui est l'objet complet d'un sous-objet auquel le temporaire est lié persiste pendant toute la durée de vie de la référence, sauf indication contraire ci-dessous. Une liaison temporaire à un membre de référence dans l’initialisateur ctor du constructeur (§12.6.2 [class.base.init]) persiste jusqu’à ce que le constructeur se ferme. Une liaison temporaire à un paramètre de référence dans un appel de fonction (§5.2.2 [expr.call]) persiste jusqu'à la fin de l'expression complète contenant l'appel.

148
Potatoswatter

Voici le moyen le plus simple d'expliquer ce qui s'est passé:

Dans main (), vous avez créé une chaîne et l'avez transmise au constructeur. Cette instance de chaîne n'existait que dans le constructeur. Dans le constructeur, vous avez affecté un membre pour qu'il pointe directement vers cette instance. Lorsque la portée a quitté le constructeur, l'occurrence de chaîne a été détruite et le membre a ensuite indiqué un objet chaîne qui n'existait plus. Si Sandbox.member pointe vers une référence hors de sa portée, ces instances externes ne seront pas incluses dans la portée.

Si vous souhaitez que votre programme affiche le comportement souhaité, apportez les modifications suivantes:

int main()
{
    string temp = string("four");    
    Sandbox sandbox(temp);
    cout << sandbox.member << endl;
    return 0;
}

Maintenant, temp passera hors de portée à la fin de main () au lieu de à la fin du constructeur. Cependant, c'est une mauvaise pratique. Votre variable membre ne doit jamais être une référence à une variable qui existe en dehors de l'instance. En pratique, vous ne savez jamais quand cette variable sera hors de portée.

Ce que je recommande, c'est de définir Sandbox.member comme un const string member;. Cela copiera les données du paramètre temporaire dans la variable membre au lieu d'attribuer la variable membre comme paramètre temporaire lui-même.

20
Squirrelsama

Techniquement, ce programme n'est pas obligé de générer quoi que ce soit sur la sortie standard (qui est un flux mis en mémoire tampon pour commencer).

  • Le bit cout << "The answer is: " émettra "The answer is: " dans le buffer de stdout.

  • Ensuite, le bit << sandbox.member fournira la référence en suspens dans operator << (ostream &, const std::string &), qui appelle comportement non défini .

Pour cette raison, rien n'est garanti. Le programme peut fonctionner apparemment bien ou peut planter sans même vider stdout - ce qui signifie que le texte "La réponse est:" ne pourrait pas apparaître sur votre écran.

4
Tanz87

Parce que votre chaîne temporaire est sortie de la portée une fois le constructeur Sandbox renvoyé et que la pile qu'il occupait a été récupérée à d'autres fins.

En règle générale, vous ne devriez jamais conserver de références à long terme. Les références sont bonnes pour les arguments ou les variables locales, jamais les membres de la classe.

0
Fyodor Soikin

réponse simple: vous faites référence à quelque chose qui a disparu. Ce qui suit fonctionnera

#include <string>
#include <iostream>
using namespace std;

class Sandbox
{

public:
    const string member = " ";
    Sandbox(const string& n) : member(n) {}//a copy is made

};

int main()
{
    Sandbox sandbox(string("four"));
    cout << "The answer is: " << sandbox.member << endl;
    return 0;
}
0
Prab