Le code suivant est assez trivial et je m'attendais à ce qu'il se compile correctement.
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 segfaut sur ce code ( http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57770 ), les autres compilateurs testés donnent des messages d'erreur qui n'expliquent pas grand chose.
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())
^
Rendre ce code compilable est possible et ne devrait faire aucune différence. Il existe 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
};
Ce code est-il vraiment incorrect ou les compilateurs se trompent-ils?
c++
c++11
language-lawyer
etam1024
la source
la source
internal compiler error: Segmentation fault
à ce code ...int i = 0
moins que ce ne soit le casstatic const int i = 0
.B()
comme un appel de fonction à un constructeur. Vous n'appelez jamais directement un constructeur. Considérez cela comme une syntaxe spéciale qui crée un temporaireB
... et le constructeur est invoqué comme une partie de ce processus, au plus profond du mécanisme qui suit.B
semble faire fonctionner celagcc 4.7
.Réponses:
Eh bien non plus. Le standard a un défaut - il dit que les deux
A
sont considérés comme terminés lors de l'analyse de l'initialiseur pourB::i
, et queB::B()
(qui utilise l'initialiseur pourB::i
) peuvent être utilisés dans la définition deA
. C'est clairement cyclique. Considère ceci:Cela a une contradiction:
B::B()
est implicitementnoexcept
ssiA()
ne lance pas etA()
ne lance pas ssiB::B()
n'est pasnoexcept
. Il existe un certain nombre d'autres cycles et contradictions dans ce domaine.Ceci est suivi par les problèmes principaux 1360 et 1397 . Notez en particulier cette note dans le numéro central 1397:
C'est un cas particulier de la règle que j'ai implémentée dans Clang pour résoudre ce problème. La règle de Clang est qu'un constructeur par défaut par défaut pour une classe ne peut pas être utilisé avant que les initialiseurs de membres de données non statiques pour cette classe soient analysés. Par conséquent, Clang émet un diagnostic ici:
... parce que Clang analyse les arguments par défaut avant d'analyser les initialiseurs par défaut, et que cet argument par défaut exigerait que
B
les initialiseurs par défaut aient déjà été analysés (afin de les définir implicitementB::B()
).la source
C'est peut-être le problème:
Ainsi, le constructeur par défaut est généré lors de la première recherche, mais la recherche échouera car A n'est pas complètement défini et B à l'intérieur de A ne sera donc pas trouvé.
la source
B b
n'est clairement pas un problème, et trouver des méthodes explicites / un constructeur explicitement déclaré dansB
n'est pas un problème. Il serait donc bien de voir une définition de la raison pour laquelle la recherche devrait se dérouler différemment ici afin que "l'B
intérieurA
ne soit pas trouvé" dans un seul cas mais pas dans les autres, avant de pouvoir déclarer le code illégal pour cette raison.=0
dei = 0;
. Mais sans cela=0
, le code est valide et vous ne trouverez pas un seul compilateur qui se plaint de l'utilisationB()
dans la définition deA
.