Per la seconda domanda, è probabilmente perché non si causa implicitamente definito. Se il costruttore viene semplicemente dichiarato implicitamente, non vi è alcun errore. Esempio:
struct A { A(int); };
struct B : A { };
// goes fine up to here
// not anymore: default constructor now is implicitly defined
// (because it's used)
B b;
Per la prima domanda, dipende dal nome del compilatore. Non ho idea di quello che la norma specifica, ma questo codice per esempio è corretto perché il nome della classe esterna (al posto del nome classe ereditata) è accessibile:
class A {};
class B: private virtual A {};
class C: public B { C(): ::A() { } }; // don't use B::A
Forse lo standard è underspecified a questo punto. Dovremo guardare.
Non sembra esserci alcun problema con il codice. Inoltre, vi è indicazione che il codice è valido. Il subobject di classe base (virtuale) viene inizializzato di default - non esiste un testo che implichi che la ricerca del nome per il nome della classe sia dine all'interno dello scope di C
.Ecco cosa dice la norma:
12.6.2/8
(C++ 0x)
Se un dato membro di dati non statico o classe di base non è nominato da un mem-inizializzatore-id (compreso il caso dove non c'è mem-inizializzatore-lista, perché il costruttore non ha ctor-inizializzatore) e l'entità non è una classe base virtuale di una classe astratta
[...] in caso contrario, l'entità è default-inizializzato
E C++ 03 ha un testo simile (meno testo è chiaro - dice semplicemente che il suo costruttore predefinito è chiamato in un posto, e in un altro lo fa dipendere dal fatto che la classe sia un POD). Affinché il compilatore inizializzi di default il subobject, deve semplicemente chiamare il suo costruttore di default - non è necessario prima cercare il nome della classe base (lo conosce già quale base è considerata).
Considerate questo codice che certamente è destinato ad essere valida, ma che avrebbe fallire se questo sarebbe essere fatto (vedi 12.6.2/4
in C++ 0x)
struct A { };
struct B : virtual A { };
struct C : B, A { };
C c;
Se costruttore di default del compilatore avrebbe semplicemente guardare -up nome classe A
all'interno di C
, avrebbe un risultato di ricerca ambiguo in relazione a ciò che subobject deve inizializzare, poiché vengono trovati sia i nomi di classe non virtuali A
sia quelli virtuali A
. Se il tuo codice è destinato a essere mal formato, direi che lo standard ha certamente bisogno di essere chiarito.
per il costruttore, notare cosa 12.4/6
dice circa il distruttore di C
:
Tutti i distruttori sono chiamati come se fossero riferimento con un nome qualificato, cioè, ignorando eventuali distruttori imperativi virtuali in più classi derivate.
Questo può essere interpretato in due modi:
- chiamare A :: ~ A()
- chiamando :: :: A ~ A()
Sembra io che lo standard è meno chiaro qui. Il secondo modo lo renderebbe valido (da 3.4.3/6
, C++ 0x, poiché entrambi i nomi di classe A
vengono cercati in ambito globale), mentre il primo lo renderà non valido (perché entrambi i nomi di classe ereditata saranno entrambi A
). Dipende anche da ciò subobject della ricerca inizia (e credo che dovremo usare la classe base virtuale 'subobject come punto di partenza). Se questo va come
virtual_base -> A::~A();
Poi ci sarà direttamente trovare il 'nome della classe come un nome pubblico, perché non dovremo passare attraverso i derivati della classe base virtuale ambiti e trovare il nome come non accessibili. Di nuovo, il ragionamento è simile.Considerare:
struct A { };
struct B : A { };
struct C : B, A {
} c;
Se il distruttore sarebbe semplicemente chiamare this->A::~A()
, questo invito non sarebbe valida a causa del risultato di ricerca ambiguo A
come un nome di classe ereditata (non si può fare riferimento a qualsiasi membro-funzione non-statico del oggetto classe base diretta dall'ambito C
, vedere 10.1/3
, C++ 03). Dovrà identificare in modo univoco i nomi delle classi che sono coinvolti e deve iniziare con la classe 'riferimento subobject come a_subobject->::A::~A();
.
Con g ++ 4.4 viene compilato. Mentre non sono stato in grado di trovare un riferimento autorevole, il mio credo è che dovrebbe essere compilato. La 'classe' più derivata può costruire il sottooggetto di tipo' A'. Si noti che esistono implementazioni per sigillare l'ereditarietà in base alla combinazione dell'eredità 'privata virtuale' * insieme * con un costruttore privato in' A' e accesso concesso a 'B' attraverso l'amicizia. Tutta la complicazione non sarebbe necessaria se fosse sufficiente utilizzare l'ereditarietà privata virtuale. –
@ DavidRodríguez-dribeas "_Tutte le complicazioni non sarebbero necessarie se fosse sufficiente l'utilizzo dell'eredità virtuale privata." "Nessuno qui ha affermato che l'idioma di suggellamento funziona senza un amministratore privato. Nell'idioma di suggellamento, l'ereditarietà privata non è necessaria, ma è necessaria per rendere l'uso dell'idioma un dettaglio di implementazione. – curiousguy