Erreur lors de l'utilisation en classe de l'initialisation de la non-membre de données statiques et imbriquées constructeur de la classe
Le code suivant est assez banal et j'attendais qu'il doit compiler amende.
struct A
{
struct B
{
int i = 0;
};
B b;
A(const B& _b = B())
: b(_b)
{}
};
J'ai testé ce code avec g++ version 4.7.2, 4.8.1, clang++ 3.2 et 3.3. Hormis le fait que g++ 4.7.2 de segmentation sur ce code (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57770), les autres testés compilateurs donner les messages d'erreur que de ne pas expliquer beaucoup de.
g++ 4.8.1:
test.cpp: In constructor ‘constexpr A::B::B()’:
test.cpp:3:12: error: constructor required before non-static data member for ‘A::B::i’ has been parsed
struct B
^
test.cpp: At global scope:
test.cpp:11:23: note: synthesized method ‘constexpr A::B::B()’ first required here
A(const B& _b = B())
^
clang++ 3.2 et 3.3:
test.cpp:11:21: error: defaulted default constructor of 'B' cannot be used by non-static data member initializer which appears before end of class definition
A(const B& _b = B())
^
Du présent code compilable est possible et semble comme il devrait pas faire de différence. Il y a deux options:
struct B
{
int i = 0;
B(){} //using B()=default; works only for clang++
};
ou
struct B
{
int i;
B() : i(0) {} //classic c++98 initialization
};
Est ce code vraiment incorrect ou sont les compilateurs de mal?
Mon G++ 4.7.3 dit
(erreur C2864: "A::B::i' : seulement static const partie intégrante de membres de données peuvent être initialisées dans une classe) est ce que VC2010 dit. Cette sortie est d'accord avec g++. Clang, dit-elle, si elle fait beaucoup moins de sens. Vous ne pouvez pas par défaut d'une variable dans une struct en faisant
BTW, je voudrais éviter la tentation de penser de l'expression
Hmm, l'ajout d'un constructeur à
Fait intéressant, le déplacement de la définition d'Un constructeur d'Une aussi semble pour le faire fonctionner (g++ 4.7); qui s'accorde avec "par défaut dans le constructeur par défaut ne peut pas être utilisé... avant la fin de la définition de la classe".
internal compiler error: Segmentation fault
à ce code...(erreur C2864: "A::B::i' : seulement static const partie intégrante de membres de données peuvent être initialisées dans une classe) est ce que VC2010 dit. Cette sortie est d'accord avec g++. Clang, dit-elle, si elle fait beaucoup moins de sens. Vous ne pouvez pas par défaut d'une variable dans une struct en faisant
int i = 0
, sauf si elle est static const int i = 0
.BTW, je voudrais éviter la tentation de penser de l'expression
B()
comme un appel de fonction à un constructeur. Vous jamais directement "appel" d'un constructeur. Penser à cela comme une syntaxe spéciale qui crée un temporaire B
... et le constructeur est appelé comme une partie seulement de ce processus, profondément à l'intérieur du mécanisme que la façon suivante.Hmm, l'ajout d'un constructeur à
B
semble faire ce travail dans gcc 4.7
.Fait intéressant, le déplacement de la définition d'Un constructeur d'Une aussi semble pour le faire fonctionner (g++ 4.7); qui s'accorde avec "par défaut dans le constructeur par défaut ne peut pas être utilisé... avant la fin de la définition de la classe".
OriginalL'auteur etam1024 | 2013-07-02
Vous devez vous connecter pour publier un commentaire.
Bien, ni. Le standard a un défaut-il est dit à la fois que
A
est considérée comme complète lors de l'analyse de l'initialiseur deB::i
, et queB::B()
(qui utilise l'initialiseur deB::i
) peut être utilisé dans la définition deA
. C'est clairement cyclique. Considérez ceci:Cela a une contradiction:
B::B()
est implicitementnoexcept
iffA()
ne les jetez pas, etA()
ne pas jeter de l'iffB::B()
est pasnoexcept
. Il y a un certain nombre d'autres cycles et des contradictions dans ce domaine.Ce suivi est assuré par les questions fondamentales Mille trois cent soixante et Mille trois cent quatre vingt dix sept. Remarque, en particulier, la présente note dans le cœur de la question 1397:
C'est un cas particulier de la règle que j'ai mis en place dans Clang pour résoudre ce problème. Clang la règle est qu'un défaut constructeur par défaut pour une classe ne peut pas être utilisé avant que la non-membre de données statiques initialiseurs de cette classe sont analysées. Donc Clang émet un diagnostic ici:
... parce que Clang analyse des arguments par défaut avant d'analyse par défaut initialiseurs, et cet argument par défaut nécessiterait
B
par défaut de les initialiseurs d'avoir déjà été analysé (dans l'ordre implicite de définirB::B()
).Saviez-vous cela en raison d'un particulier de l'expérience du passé avec ce problème, ou tout simplement par la lecture attentive de la norme (et la liste des défauts)? Aussi, +1.
+1 pour cette réponse détaillée. Donc, ce serait le moyen de sortir? Une sorte de "2-phase de classe d'analyse", où l'analyse de l'extérieur de la classe des membres qui dépendent de l'intérieur des classes est retardée jusqu'à l'intérieur des classes ont été entièrement formé?
Oui, le diagnostic ici n'est pas très bonne. J'ai déposé llvm.org/PR16550 pour cela.
J'ai découvert ce problème lors de la mise en œuvre des initialiseurs pour les non-membres de données statiques dans Clang.
OriginalL'auteur Richard Smith
C'est peut-être le problème:
Ainsi, le constructeur par défaut est généré lors de la première regardé, mais la recherche échouera parce que l'Un n'est pas complètement défini et B à l'intérieur d'Une aura donc pas être trouvé.
B b
n'est pas un problème, et de trouver des méthodes explicites/ un déclarées explicitement un constructeur dansB
n'est pas un problème. Donc, il serait agréable de voir une partie de la définition de pourquoi la recherche doit procéder différemment ici, de sorte que "B
à l'intérieur deA
n'est pas trouvé" dans un cas, mais pas les autres, avant de nous déclarer le code illégal pour cette raison.J'ai trouvé des mots dans la norme que la définition de la classe est considérée comme complète lors de l'initialisation de classe, y compris au sein des classes imbriquées. Je n'ai pas pris la peine d'enregistrer la référence comme il ne semble pas pertinent.
l'énoncé dit implicitement par défaut des constructeurs sont définis lors de l'odr. est expressément définie après la première déclaration. Et B b ne prend pas l'appel d'un constructeur, le constructeur de A appelle le constructeur de B
Si quelque chose comme cela était le problème, le code serait encore valide si vous supprimez le
=0
dei = 0;
. Mais sans que=0
, le code est valide et vous ne trouverez pas un seul compilateur qui se plaint de l'aideB()
dans la définition deA
.OriginalL'auteur fscan