2015-08-16 13 views
6

Facendo seguito a this question about multiple (virtual) inheritance, mi piacerebbe chiedere informazioni su un semplice MWE che rende g ++ 5.2.0 sconvolto mentre clang ++ 3.6.2 gestisce proprio bene, con nessuna lamentela tutto, anche con -Wall e -Wextra set. Quindi, ecco la MWE:eredità virtuale e l'inizializzazione uniforme in C++

class Z {}; 
class A : virtual Z { protected: A() {} }; 
class B : virtual Z { protected: B() {} }; 
class C : A, B { public: C() : A{}, B{} {} }; 
int main() { C c{}; return 0; } 

differenza clang ++, g ++ lamenta come questo:

gccodd.c++: In constructor ‘C::C()’: 
gccodd.c++:2:34: error: ‘A::A()’ is protected 
class A : virtual Z { protected: A() {} }; 
           ^
gccodd.c++:4:39: error: within this context 
class C : A, B { public: C() : A{}, B{} {} }; 
            ^
gccodd.c++:3:34: error: ‘B::B()’ is protected 
class B : virtual Z { protected: B() {} }; 
           ^
gccodd.c++:4:39: error: within this context 
class C : A, B { public: C() : A{}, B{} {} }; 
            ^

Sostituzione l'inizializzazione divisa nel costruttore di C con il vecchio modulo funziona bene e anche se sia clang ++ e g ++ sono felici con il seguente:

class C : A, B { public: C() : A(), B() {} }; 

Questo produce le due opzioni ovvie:

  1. Il codice viola lo standard in qualche modo, rendendo l'esito indefinito (vale a dire, qualsiasi risultato sarebbe accettabile).
  2. Uno dei due compilatori presenta un bug relativo all'inizializzazione uniforme e all'ereditarietà multipla + virtuale.

Se si trattasse di una questione di voto, (1) potrebbe vincere, perché ICPC 15.0.0 dice il seguente:

gccodd.c++(4): error #453: protected function "A::A()" (declared at line 2) is not accessible through a "A" pointer or object 
    class C : public virtual A, public virtual B { public: C() : A{}, B{} {} }; 
                   ^

gccodd.c++(4): error #453: protected function "B::B()" (declared at line 3) is not accessible through a "B" pointer or object 
    class C : public virtual A, public virtual B { public: C() : A{}, B{} {} }; 
                    ^

Quindi, è (1) o (2)? E se è il caso precedente, allora cosa c'è che non va nel mio MWE?

+0

Sembra un bug. Non c'è motivo di pensare che ci sarebbe UB qui. –

+0

VC++ 14.0 lo compila bene anche se IntelliSense si lamenta. – Fireho

+2

GCC è bacato quando si tratta di [inizializzazione di aggregazione delle classi di base in un inizializzatore di mem] (http://coliru.stacked-crooked.com/a/8c60d42406381caa). – 0x499602D2

risposta

5

List-inizializzazione di un oggetto o di riferimento di tipo T è definito come segue:
(3.1) - Se T è un tipo di classe e la lista di inizializzazione ha un singolo elemento di tipo cvU [ ..]
(3.2) - Altrimenti, se T è un array di caratteri [..]
(3.3) - Altrimenti, se T è un aggregato, viene eseguita l'inizializzazione di aggregazione (8.5.1).
(3.4) - Altrimenti, se l'elenco di inizializzazione non ha elementi e T è un tipo di classe con un costruttore predefinito , l'oggetto è inizializzato a valore.

A e B entrambi hanno classi di base e quindi non sono aggregati. Quindi si applica il quarto punto elenco. E così abbiamo lo stesso effetto esattamente come se avessimo usato ():

Un oggetto le cui inizializzatore è un insieme vuoto di parentesi, cioè (), sarà valore-inizializzato.

Qualsiasi compilatore che produca risultati diversi con tali inizializzatori non può essere conforme.

§11.4, che gestisce l'accesso ai membri protected, non menziona alcunché relativo al modulo di inizializzazione.Tuttavia, per quanto riguarda l'inizializzazione delle basi in un inizializzatore di memo nel costruttore, §11.4 al momento è difettoso come menzionato dal numero #1883 di CWG.

+0

Grazie per aver indicato la parte esatta dello standard che descrive questo caso. Quindi sembra che il compilatore Intel/"compositore" prenda la sua compatibilità GCC in qualche modo troppo seriamente, implementando anche i bug di GCC. :-) Per quanto riguarda i bug nello standard, ho recentemente individuato [questa domanda sui riferimenti e l'inizializzazione uniforme] (http://stackoverflow.com/questions/10509603/why-cant-i-initialize-a-reference-in -un-inizializzatore-list-con-divisa-initializ). Fondamentalmente una trasposizione di 2 punti elenco nello standard ha aperto una finestra di 3 anni di compilatori di buggy (in un certo senso). – user1715611