Considérer:
#include <string>
#include <iostream>
class Foo
{
public:
Foo( char const * msg ) : x( y )
{
y = msg;
}
std::string const & x;
private:
std::string y;
};
int main( int argc, char * argv[] )
{
if ( argc >= 2 )
{
Foo f( argv[1] );
std::cout << f.x << std::endl;
}
}
Cela compile et affiche le premier paramètre ... mais je doute que ce soit réellement "légal"/bien formé. Je sais que la liste d'initialisation doit initialiser les variables dans l'ordre de leur déclaration dans la classe, de manière à ne pas référencer les variables qui n'ont pas encore été initialisées. Mais qu'en est-il des variables membres pas dans la liste d'initialisation ? Puis-je créer en toute sécurité des références à ceux-ci?
(L’exemple n’a naturellement aucun sens. C’est juste pour clarifier ce dont je parle.)
Tu peux le faire1 parce que:
x
et y
sont déjà dans la portée ( [basic.scope.class]/1 ).y
est déjà obtenu ( [basic.life]/7 ), cette référence peut être liée à y
.L'utilisation de cette référence dans l'instruction composée du constructeur (une fois l'initialisation du membre terminée) convient également. En effet, y
est considéré comme étant initialisé et x
fait maintenant référence à un objet dont la durée de vie a commencé.
1 - Il y a une mise en garde pour les avocats de la langue. Techniquement, une référence doit être liée à un objet valide ( [dcl.ref]/5 ), c'est-à-dire dont le cycle de vie a commencé. Cependant, comme Core Language numero 363 details, il devrait fonctionner! La formulation problématique et une résolution possible sont discutées dans Core Language issue 453 (avec la permission de @ T.C. Dans un commentaire supprimé). Il y a un bogue dans la norme, mais votre code est censé être bien formé, et les implémentations en sont généralement conscientes .
Mais qu'en est-il des variables membres qui ne figurent pas dans la liste d'initialisation?
Que les variables soient ou non dans la liste d'initialisation, est sans importance à cet égard. Si une variable ne figure pas dans la liste d'initialisation (ni d'initialiseur de membre par défaut), elle est initialisée par défaut.
y
est initialisé après x
. Ce n'est pas à cause de la liste d'initialisation des membres, car la liste d'initialisation des membres n'affecte pas l'ordre d'initialisation des membres. Les membres sont initialisés dans l'ordre de leur déclaration.
Cependant, si y
est initialisé ou non est également sans importance. Il est bien formé pour lier une référence à un membre avant que le membre ne soit initialisé (à l'exception de la liaison à une base virtuelle de membre non initialisé, qui aurait UB).
En ce qui concerne sécurité (ou peut-être exactitude plus précisément), je vous recommande de prendre le temps de réfléchir à ce qui se passe lorsque Foo
est copié. À quoi x
fera-t-il référence? Est-ce ce que l'utilisateur de la classe s'attendrait?
Puis-je créer en toute sécurité des références à ceux-ci?
Oui, vous pouvez. L'adresse de stockage du membre y
est connue, qu'elle soit initialisée ou non. L'initialisation de la référence x(y)
est donc légale.