2010-06-22 6 views
7

Nous savons tous que les choses de ce genre sont valides en C++:référence à const bizarrerie temporaire

const T &x = T(); 

tout:

T &x = T(); 

est pas.

Dans a recent question, la conversation mène à cette règle. L'OP avait posté du code qui évoque clairement UB. Mais je devrais attendre une version modifiée de celui-ci au travail (Ceci est la version modifiée):

#include <iostream> 
using namespace std; 

class A { 
public: 
    A(int k) { _k = k; }; 
    int get() const { return _k; }; 
    int _k; 
}; 

class B { 
public: 
    B(const A& a) : _a(a) {} 
    void b() { cout << _a.get(); } 
    const A& _a; 
}; 

B* f() { 
    return new B(A(10)); 
} 

int main() { 
    f()->b(); 
} 

Ce imprime des documents inexploitables sur certaines machines, 10 sur les autres ... sonne comme UB pour moi :-). Mais alors je pensais, bien A est fondamentalement glorifié int tout ce qu'il en fait initialiser un et le lire. Pourquoi ne pas simplement appeler A un int et voir ce qui se passe:

#include <iostream> 
using namespace std; 

typedef int A; 

class B { 
public: 
    B(const A& a) : _a(a) {} 
    void b() { cout << _a; } 
    const A& _a; 
}; 

B* f() { 
    return new B(A(10)); 
} 

int main() { 
    f()->b(); 
} 

Il imprime 10 chaque fois. Il au moins semble comme la règle de référence const est en vigueur pour la version int, mais pas pour la version de classe. Sont-ils tous deux simplement UB en raison de l'utilisation du tas? Suis-je juste chanceux avec la version int parce que la compilation a vu à travers tous const s et a juste imprimé directement un 10? Quel aspect de la règle me manque?

Répondre

15

Cela démontre simplement que l'analyse du comportement de la langue en "l'essayant dans le compilateur" ne produit normalement aucun résultat utile. Vos deux exemples sont invalides pour la même raison.

La durée de vie du temporaire n'est prolongée que lorsque vous utilisez ce temporaire comme initialiseur direct pour une référence const - uniquement pour établir un lien "à vie" entre la référence et le temporaire.

Essayer de passer un paramètre temporaire en tant qu'argument d'un constructeur et d'attacher une référence const à l'intérieur du constructeur n'établira pas le lien susmentionné et ne prolongera pas la durée de vie du temporaire.

En outre, conformément à la norme C, si vous le faites

struct S { 
    const int &r; 

    S() : r(5) { 
    cout << r; // OK 
    } 
}; 

la durée de vie de la seule temporaire est prolongée jusqu'à la fin du constructeur. Une fois que le constructeur est terminé, les matrices temporaires, ce qui signifie que cette

S s; 
cout << s.r; // Invalid 

est invalide.

Votre expérience avec int simplement "semble fonctionner", purement par accident.

+0

Je vous suggère de remplacer "une référence const" par "une référence const locale *" pour rendre les choses encore plus claires;) – fredoverflow

+0

@FredOverflow: Pourquoi insistez-vous sur une référence * locale *? Vous pouvez légalement déclarer 'const std :: string & r =" Hello "' à la portée de l'espace de noms et obtenir la même extension de durée de vie. – AnT

+0

@AndreyT, comment ce * lien * s'établit-il à quel niveau? –

3

Vous venez d'avoir de la chance. Modification de B :: b à ceci:

void b() { 
    int i = rand(); 
    int j = rand(); 
    cout << _a << endl; 
} 

affiche des nombres aléatoires.

+0

Oups, mal édité post, reculé :) – fredoverflow

3

Il imprime 10 à chaque fois.

Modifier la fonction principale un peu et il ne sera pas imprimer 10 plus:

int main() 
{ 
    B* p = f(); 
    cout << "C++\n"; // prints C++ 
    p->b();   // prints 4077568 
} 

Comment ce soit établie à quel niveau?

Voir 12.2 [class.temporary] §4 et §5:

objets temporaires sont détruits comme la dernière étape dans l'évaluation de la pleine expression (lexicalement) contient le point où ils ont été créés .

Il existe deux contextes dans lesquels les temporaires sont détruits à un point différent de la fin de l'expression complète. Le premier contexte est [...]

Le deuxième contexte est lorsqu'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 la référence est liée persiste pendant toute la durée de la référence sauf: [...]

Une liaison temporaire à un paramètre de référence dans un appel de fonction persiste jusqu'à la fin de l'expression complète contenant l'appel.

Donc, dans votre cas, le temporaire est détruit après l'évaluation de la pleine expression new B(A(10)).

Questions connexes