Poniższy kod jest dość trywialny i spodziewałem się, że powinien się dobrze skompilować.
struct A
{
struct B
{
int i = 0;
};
B b;
A(const B& _b = B())
: b(_b)
{}
};
Przetestowałem ten kod z g ++ w wersji 4.7.2, 4.8.1, clang ++ 3.2 i 3.3. Oprócz tego, że g ++ 4.7.2 segfaults w tym kodzie ( http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57770 ), inne testowane kompilatory wyświetlają komunikaty o błędach, które niewiele wyjaśniają.
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 i 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())
^
Możliwość kompilacji tego kodu jest możliwa i wydaje się, że nie powinno to robić żadnej różnicy. Istnieją dwie możliwości:
struct B
{
int i = 0;
B(){} // using B()=default; works only for clang++
};
lub
struct B
{
int i;
B() : i(0) {} // classic c++98 initialization
};
Czy ten kod jest naprawdę nieprawidłowy, czy kompilatory są złe?
c++
c++11
language-lawyer
etam1024
źródło
źródło
internal compiler error: Segmentation fault
do tego kodu ...int i = 0
to, chyba że tak jeststatic const int i = 0
.B()
jako wywołania funkcji do konstruktora. Ty nigdy bezpośrednio „wezwanie” konstruktor. Pomyśl o tym jako o specjalnej składni, która tworzy tymczasowyB
... a konstruktor jest wywoływany jako tylko jedna część tego procesu, głęboko w mechanizmie, który następuje.B
wydaje się działać w programiegcc 4.7
.Odpowiedzi:
Cóż, nie. Standard ma wadę - mówi, że zarówno to
A
jest uważane za zakończone podczas analizowania inicjatora dlaB::i
, jak i toB::B()
(które używa inicjatora dlaB::i
) może być użyte w definicjiA
. To wyraźnie cykliczne. Rozważ to:struct A { struct B { int i = (A(), 0); }; A() noexcept(!noexcept(B())); };
To ma sprzeczności:
B::B()
jest niejawnienoexcept
IFFA()
nie rzucać, aA()
nie rzucać IFFB::B()
to nienoexcept
. Istnieje wiele innych cykli i sprzeczności w tej dziedzinie.Jest to śledzone przez podstawowe problemy 1360 i 1397 . Zwróćcie w szczególności uwagę na tę uwagę w numerze podstawowym 1397:
To szczególny przypadek reguły, którą zaimplementowałem w Clang, aby rozwiązać ten problem. Reguła Clanga polega na tym, że domyślny konstruktor domyślny dla klasy nie może być używany przed przeanalizowaniem niestandardowych inicjatorów elementu członkowskiego danych dla tej klasy. Dlatego Clang wydaje tutaj diagnostykę:
A(const B& _b = B()) ^
... ponieważ Clang analizuje domyślne argumenty, zanim
B
przeanalizuje domyślne inicjatory, a ten domyślny argument wymagałby , aby domyślne inicjatory zostały już przeanalizowane (w celu niejawnego zdefiniowaniaB::B()
).źródło
Może to jest problem:
Tak więc domyślny konstruktor jest generowany podczas pierwszego wyszukiwania, ale wyszukiwanie zakończy się niepowodzeniem, ponieważ A nie jest w pełni zdefiniowane, a zatem B wewnątrz A nie zostanie znalezione.
źródło
B b
nie stanowi to problemu, a znalezienie jawnych metod / jawnie zadeklarowanego konstruktora w programieB
nie stanowi problemu. Więc byłoby miło zobaczyć jakąś definicję, dlaczego wyszukiwanie powinno przebiegać tutaj inaczej, tak aby „B
wewnątrzA
nie zostało znalezione” tylko w tym jednym przypadku, ale nie w innych, zanim będziemy mogli z tego powodu zadeklarować kod jako nielegalny.=0
fromi = 0;
. Ale bez tego=0
kod jest prawidłowy i nie znajdziesz ani jednego kompilatora, który narzekałby na używanieB()
w definicjiA
.