2017-03-11 66 views
3

我想實現下面的設計,這是一個dreaded diamond情況:爲什麼需要超類空的構造函數,但沒有在可怕的鑽石情況下調用?

struct super_base 
{ 
    super_base(int a) { b = a; } 
    int b; 
}; 

struct base : virtual super_base 
{}; 

struct other_base : virtual super_base 
{}; 

struct derived : base, other_base 
{ 
    derived(int a) : super_base{a} {} 
}; 

int main() {} 

不工作。使用鏘上面的代碼錯誤是相當 善於解釋的錯誤:

error: call to implicitly-deleted default constructor of 'base' 
    derived(int a) : super_base{a} {} 
^
note: default constructor of 'base' is implicitly deleted because base 
     class 'super_base' has no default constructor 

所以我添加一個空的構造爲super_base,和它的作品:

#include<iostream> 
#include<stdexcept> 

struct super_base 
{ 
    super_base() { throw std::logic_error{"Constructor should not be called."}; }; 
    super_base(int a) { b = a; } 
    int b; 
}; 

struct base : virtual super_base 
{}; 

struct other_base : virtual super_base 
{}; 

struct derived : base, other_base 
{ 
    derived(int a) : super_base{a} {} 
}; 

int main() { std::cout << derived(10).b << '\n'; } 

但爲什麼這個不能丟?

P.S .:我故意用一個可怕的菱形圖案來說明虛擬繼承的用法。單一繼承的問題仍然存在。

P.P.S .:使用的編譯器是Clang 3.9.1。結果與GCC 6.3.1的 相同。

+0

感謝@Yakk的回答,我現在對問題有了更清晰的認識。我認爲它來自編譯器階段的順序。 必須有: (1)第一階段選擇所有需要的構造(如果需要建立隱構造函數) (2),那麼階段丟棄不需要的構造函數調用。 這可以解釋爲什麼編譯器會嘗試構建它不需要的構造函數 。 我不知道編譯器是否嚴格遵循這個 點上的C++ 14標準。有一天我應該深入研究標準和編譯器的代碼! – turfu

回答

6
struct base:super_base {}: 

這試圖創建一些構造函數。其中一個它試圖創建的是base::base()

如果super_base沒有super_base::super_base(),則此構造函數被刪除。

如果我們有super_base::super_base()=default{},那麼base::base()默認存在,即使你沒有=default它。

同樣的事情發生在other_base

而你的派生類試圖調用基礎對象的構造函數,這些構造函數被刪除,這會給你一個錯誤。

現在,爲什麼不叫它?當你有一個虛擬的基類時,構造函數只被調用一次。調用虛擬基類構造函數的中間類型忽略它們的調用。

所以我們有derived()調用base(),base()調用super_base(),但該調用被忽略,因爲虛擬繼承。

derived()代替使用super_base(int)的呼叫。

現在,爲什麼這些規則?因爲C++沒有「只有在你明確調用特定的基類構造函數的這個類的派生類時才能調用的構造函數」的概念。虛擬繼承並不像您想要的那樣完整。

相關問題