5

在以下代碼中,編譯器正在請求基類類X默認構造爲。但是,如果我從類節點後,進入該成員m_x變,當然,曖昧的繼承刪除虛擬關鍵字,但類X默認構造函數不再需要。虛擬繼承是否強制基類成爲默認可構造的?

這是什麼原因?

#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

這幾乎是一個*最小*的例子。我可以從這裏刪除約100行代碼! – Barry

+0

@抱歉抱歉,但我認爲保持示例有效的唯一多餘的事情是基類C.我希望代碼清晰,即使它比最小值長一點。 – nyarlathotep108

+1

只要'A','X'和'Node '就足夠了(不需要'Apply','Inheritance','B','C'或'Example' ......) – Barry

回答

0

從@Berry答案開始,修復代碼的唯一方法是代碼,以虛擬繼承X構造函數的顯式調用。

然而,這是不夠的顯式調用X的建設類一個,或Ç:必須基本上叫每個類在任何級別參與繼承!

最棘手的是繼承<> variadic模板類:可變擴展的每一步都必須提供對X構造函數的顯式調用。

以下是在MinGW 4.9上運行的代碼。2與啓用C++ 11標誌:

#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

初始化順序的一類是這樣的[class.base.init]:

在非委託構造,初始化進行以下順序:
- 首先,與只對最大派生類(1.8)的構造函數,虛擬基類在 中初始化它們出現在基類的有向無環圖的深度優先從左到右的遍歷中的順序,其中「left-to -right「是派生類base-specifier-li中基類的出現順序ST。

你的層次是A --> Node<X> --> X,因此要獲得初始化的第一件事是X,因爲它是一個虛基類。這不是在你的MEM-初始值設定,所以隱含的默認構造插入:

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

} 

由於X沒有缺省構造的,你得到這個錯誤。你可以只用顯式地提供正確的事情解決這個問題:

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

您不必擔心X正在建設的兩倍,因爲虛基類僅修建了派生類......這將是A而不是Node<X>

+0

這還是不爲我工作。我在A,B,C甚至在Example中添加了顯式的X(i)構造函數調用,但是編譯器仍然要求我使用默認的可構造X. – nyarlathotep108

+0

@ nyarlathotep108請注意,您的繼承來自'A', '繼承' - 但它也是從'X'實際派生的 - 所以它必須在它的初始化列表結構'X'上。簡單地說你的'繼承'類模板並不是爲處理虛擬繼承而設計的...... – PiotrNycz

+0

@PiotrNycz我糾正了代碼,現在虛擬繼承被正確實現 – nyarlathotep108