5

Nel seguente codice, il compilatore richiede la classe di base X per il predefinito constructible. Tuttavia, se mi tolgo la virtuale parola chiave dall'eredità del Nodo classe, l'accesso al membro m_x diventa, ovviamente, ambiguo, ma il costruttore predefinita per classe X non è più necessaria .L'ereditarietà virtuale costringe una classe base a essere costruita in modo predefinito?

Qual è il motivo?

#include <iostream> 

struct Apply 
{ 
    template< typename T > 
    struct Node : virtual T // this line contains the virtual inheritance 
    { 
     template< typename ...Args> 
     Node(Args... args) 
      : T(args...) 
     {} 
    }; 

    template < typename ...BaseClasses> 
    struct Inheritance; 

    template < typename FirstBaseClass, typename ...OtherBaseClasses> 
    struct Inheritance< FirstBaseClass, OtherBaseClasses... > : FirstBaseClass 
      , Inheritance<OtherBaseClasses...> 
    { 
     template< typename ...Args> 
     Inheritance(Args... args) 
      : FirstBaseClass(args...) 
      , Inheritance<OtherBaseClasses...>(args...) 
     { 

     } 
    }; 
}; 

template < > 
struct Apply::Inheritance< > 
{ 
    template< typename ...Args> 
    Inheritance(Args... args){} 
}; 

struct X 
{ 
    X(int i){} 

    int m_x; 
}; 

struct A : Apply::Node<X> 
{ 
    A(int i) 
     : Apply::Node<X>(i) 
     , m_a(i) 
    { 

    } 
    int m_a; 
}; 


struct B : Apply::Node<X> 
{ 
    B(int i) 
     : Apply::Node<X>(i) 
     , m_b(i) 
    { } 

    int m_b; 
}; 

struct C : Apply::Node<X> 
{ 
    C(int i) 
     : Apply::Node<X>(i) 
     , m_c(i) 
    { } 

    int m_c; 
}; 

struct Example : Apply::Inheritance< A, B, C > 
{ 
    Example(int i) 
     : Apply::Inheritance< A, B, C >(i) 
    { } 

    void print() const 
    { 
     // this line needs the virtual inheritance 
     std::cout << m_x << std::endl; 

     std::cout << m_a << std::endl; 
     std::cout << m_b << std::endl; 
     std::cout << m_c << std::endl; 
    } 
}; 

int main() 
{ 
    Example ex(10); 

    ex.print(); 

    return 0; 
} 
+0

Questo non è un esempio * minimo *. Posso tagliare circa 100 linee di codice da qui! – Barry

+0

@Barry scusa per quello, ma penso che l'unica cosa ridondante per mantenere valido l'esempio sia la classe base C. Spero che il codice sia chiaro anche se è un po 'più lungo del minimo. – nyarlathotep108

+1

Solo 'A',' X' e 'Nodo ' sono sufficienti (non è necessario 'Apply',' Ereditarietà', 'B',' C' o 'Example' ...) – Barry

risposta

0

A partire dalla risposta @Berry, l'unico modo per correggere il codice era codificare una chiamata esplicita al costruttore di virtualizzazione ereditato virtuale X.

Tuttavia, non è sufficiente per esplicita chiamare la costruzione di X nelle classi A, B, o C: esso deve essere richiamato sostanzialmente in ogni classe coinvolti nella successione a qualsiasi livello!

Lo spinoso era il Ereditarietà <> variadic classe template: ogni fase della espansione variadic deve fornire la chiamata esplicita al costruttore X.

Questo è il codice che funziona su MinGW 4.9.2 con flag C++ 11 abilitato:

#include <iostream> 

template< typename T, typename V > 
struct Node : virtual V 
{ 
    using Virtual = V; // Added this line 

    template< typename ...Args > 
    Node(Args... args) 
     : V(args...) 
    { } 
}; 

template < typename ...BaseClasses> 
struct Inheritance; 

template < typename FirstBaseClass, typename ...OtherBaseClasses> 
struct Inheritance< FirstBaseClass, OtherBaseClasses... > 
     : FirstBaseClass 
     , Inheritance<OtherBaseClasses...> 
{ 
    template< typename ...Args> 
    Inheritance(Args... args) 
     : FirstBaseClass::Virtual(args...) // added this line 
     , FirstBaseClass(args...) 
     , Inheritance<OtherBaseClasses...>(args...) 
    { } 
}; 

template < > 
struct Inheritance< > 
{ 
    template< typename ...Args > 
    Inheritance(Args... args) 
    { } 
}; 

struct X 
{ 
    X(int i) 
     : m_x(i) 
    { } 

    int m_x; 
}; 

struct A : Node< A, X > 
{ 
    A(int i) 
     : X(i) // added this line 
     , Node< A, X >(i) 
     , m_a(i) 
    { } 

    int m_a; 
}; 


struct B : Node< B, X > 
{ 
    B(int i) 
     : X (i) // added this line 
     , Node< B, X >(i) 
     , m_b(i) 
    { } 

    int m_b; 
}; 

struct C : Node< C, X > 
{ 
    C(int i) 
     : X (i) // added this line 
     , Node< C, X >(i) 
     , m_c(i) 
    { } 

    int m_c; 
}; 

struct Example : Inheritance< A, B, C > 
{ 
    Example(int i) 
     : X (i) // added this line 
     , Inheritance< A, B, C >(i) 
    { } 

    void print() const 
    { 
     // this line needs the virtual inheritance 
     std::cout << m_x << std::endl; 

     std::cout << m_a << std::endl; 
     std::cout << m_b << std::endl; 
     std::cout << m_c << std::endl; 
    } 
}; 

int main() 
{ 
    Example ex(10); 

    ex.print(); 

    return 0; 
} 
5

L'ordinamento di inizializzazione per una classe va come questo [class.base.init]:

In un costruttore non delega, procede inizializzazione nel seguente ordine:
- Primo, e solo per il costruttore della classe più derivata (1.8), le classi di base virtuali vengono inizializzate in nell'ordine in cui appaiono su una traversata da sinistra a destra in profondità del grafico aciclico diretto delle classi di base, dove "left-to -right "è l'ordine di comparsa delle classi base nella classe derivata -specificatore-li st.

la gerarchia è A --> Node<X> --> X, quindi la prima cosa da farsi è la inizializzato X, dal momento che è una classe base virtuale. Non è specificato nel tuo mem-inizializzatore, quindi si inserisce la costruzione di default implicito:

A(int i) 
    : X() // <== implicit 
    , Node<X>(i) 
    , m_a(i) 
{ 

} 

Dal X non è predefinita costruibile, si ottiene questo errore. È possibile risolvere questo con un solo fornendo esplicitamente la cosa giusta:

A(int i) 
    : X(i) 
    , Node<X>(i) 
    , m_a(i) 
{ 

Non devi preoccuparti di X in costruzione due volte, dal momento che le classi base virtuali sono costruiti solo per la classe derivata più ... che sarebbe A e non Node<X>.

+0

Questo è ancora non funziona per me. Ho aggiunto l'esplicita chiamata di costruttori X (i) in A, B, C e anche in Esempio, ma il compilatore mi chiede ancora una X costruibile di default. – nyarlathotep108

+0

@ nyarlathotep108 nota che il tuo 'Ereditarietà ' deriva da 'A' e 'Eredità ' - ma deriva anche da 'X' - quindi deve avere nella sua lista di inizializzazione la costruzione di' X'. Semplicemente parlando il tuo modello di classe 'Inheritance' non è progettato per gestire l'ereditarietà virtuale ... – PiotrNycz

+0

@PiotrNycz Ho corretto il codice, ora l'ereditarietà virtuale è correttamente implementata – nyarlathotep108

Problemi correlati