Der folgende Code ist ziemlich trivial und ich habe erwartet, dass er gut kompiliert werden kann.
struct A
{
struct B
{
int i = 0;
};
B b;
A(const B& _b = B())
: b(_b)
{}
};
Ich habe diesen Code mit g ++ Version 4.7.2, 4.8.1, clang ++ 3.2 und 3.3 getestet. Abgesehen von der Tatsache, dass g ++ 4.7.2 Fehler in diesem Code aufweist ( http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57770 ), geben die anderen getesteten Compiler Fehlermeldungen aus, die nicht viel erklären.
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 und 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())
^
Es ist möglich, diesen Code kompilierbar zu machen, und es scheint, dass dies keinen Unterschied machen sollte. Es gibt zwei Möglichkeiten:
struct B
{
int i = 0;
B(){} // using B()=default; works only for clang++
};
oder
struct B
{
int i;
B() : i(0) {} // classic c++98 initialization
};
Ist dieser Code wirklich falsch oder sind die Compiler falsch?
c++
c++11
language-lawyer
etam1024
quelle
quelle
internal compiler error: Segmentation fault
zu diesem Code ...int i = 0
wenn dies der Fall iststatic const int i = 0
.B()
als Funktionsaufruf für einen Konstruktor zu betrachten. Sie "rufen" niemals direkt einen Konstruktor auf. Stellen Sie sich dies als eine spezielle Syntax vor, die ein temporäres Element erstelltB
... und der Konstruktor wird nur als ein Teil dieses Prozesses aufgerufen, tief innerhalb des folgenden Mechanismus.B
scheint diese Arbeit in zu machengcc 4.7
.Antworten:
Nun, auch nicht. Der Standard weist einen Fehler auf - er besagt, dass sowohl das,
A
was beim Parsen des Initialisierers als vollständig betrachtet wird, als auch das, fürB::i
dasB::B()
(der den Initialisierer verwendetB::i
) verwendet werden können, innerhalb der Definition von verwendet werden könnenA
. Das ist eindeutig zyklisch. Bedenken Sie:Dies hat einen Widerspruch:
B::B()
implizitnoexcept
iffA()
nicht werfen, undA()
wirft nicht iffB::B()
ist nichtnoexcept
. Es gibt eine Reihe anderer Zyklen und Widersprüche in diesem Bereich.Dies wird durch die Kernprobleme 1360 und 1397 verfolgt . Beachten Sie insbesondere diesen Hinweis in der Kernausgabe 1397:
Dies ist ein Sonderfall der Regel, die ich in Clang implementiert habe, um dieses Problem zu beheben. Clangs Regel lautet, dass ein standardmäßiger Standardkonstruktor für eine Klasse nicht verwendet werden kann, bevor die nicht statischen Datenelementinitialisierer für diese Klasse analysiert wurden. Daher stellt Clang hier eine Diagnose:
... weil Clang Standardargumente analysiert, bevor es Standardinitialisierer analysiert, und dieses Standardargument erfordert
B
, dass die Standardinitialisierer bereits analysiert wurden (um implizit zu definierenB::B()
).quelle
Vielleicht ist das das Problem:
Der Standardkonstruktor wird also beim ersten Nachschlagen generiert, aber das Nachschlagen schlägt fehl, da A nicht vollständig definiert ist und B in A daher nicht gefunden wird.
quelle
B b
ist eindeutig kein Problem, und das Auffinden expliziter Methoden / eines explizit deklarierten Konstruktors inB
ist kein Problem. Es wäre also schön zu sehen, warum die Suche hier anders ablaufen sollte, damit "B
innenA
nicht gefunden wird", nur in diesem einen Fall, aber nicht in den anderen, bevor wir den Code aus diesem Grund für illegal erklären können.=0
von entferneni = 0;
. Aber ohne das=0
ist der Code gültig und Sie werden keinen einzigen Compiler finden, der sich über die VerwendungB()
innerhalb der Definition von beschwertA
.