2017-07-26 83 views
0

我很難理解爲什麼我的模板層次結構不會傳遞比一層更深的構造函數參數。模板層次結構構造函數參數

到目前爲止,我讀過它可能與名稱空間或模板構造函數未命名有關。我通過範圍指令將構造函數參數傳遞給基類的各種嘗試都失敗了。

// test.cc 
#include <string> 

// Base class, obviously. 
template <typename T> 
class Base { 
public: 
    Base(const T& t) : _t(t) {} 
    virtual ~Base() { } 

    virtual T getVal() const { return _t; } 

private: 
    T _t; 
}; 

// First derivation. This works, although it is explicit. 
class DerivedOne : public virtual Base<std::string> 
{ 
public: 
    DerivedOne(const std::string& path) : Base<std::string>(path) {} 
    virtual ~DerivedOne() {} 
}; 

// Second derivation. Constructor fails to compile unless I explicitly 
// pass the 'path' argument to Base in the initializer list or create a 
// default constructor for Base (which, of course, leaves Base::_t 
// uninitialized). 
class DerivedTwo : public virtual DerivedOne 
{ 
public: 
    DerivedTwo(const std::string& path) : DerivedOne(path) {} 
    // This works 
    // DerivedTwo(const std::string& path) : DerivedOne(path), Base(path) {} 
    virtual ~DerivedTwo() {} 
}; 

int main() 
{ 
    return 0; 
} 

編譯器會抱怨:

test.cc: In constructor ‘DerivedTwo::DerivedTwo(const string&)’: 
test.cc:31:58: error: no matching function for call to ‘Base<std::__cxx11::basic_string<char> >::Base()’ 
    DerivedTwo(const std::string& path) : DerivedOne(path) {} 
                 ^
test.cc:7:5: note: candidate: Base<T>::Base(const T&) [with T = std::__cxx11::basic_string<char>] 
    Base(const T& t) : _t(t) {} 
    ^
test.cc:7:5: note: candidate expects 1 argument, 0 provided 
test.cc:5:7: note: candidate: Base<std::__cxx11::basic_string<char> >::Base(const Base<std::__cxx11::basic_string<char> >&) 
class Base { 
    ^
test.cc:5:7: note: candidate expects 1 argument, 0 provided 

爲什麼我需要聲明一個默認的構造函數時,它似乎是參數的構造函數應該叫什麼名字?爲什麼我必須顯式地將DerivedTwo的參數傳遞給Base,因爲我已將它傳遞給DerivedOne(誰應該將它傳遞給Base)?

有沒有辦法避免這種重複?這是否意味着當派生類初始化程序列表中的堆上分配了基本模板構造函數參數時,我不能使用初始化程序列表?

+0

這就是虛擬繼承的工作原理。你是否真的想在'class DerivedOne:public virtual Base '和'class DerivedTwo:public virtual DerivedOne'中使用'virtual'?請注意,不需要使用虛擬功能。 – NathanOliver

+0

在真正的單詞應用程序中,需要虛擬功能。我仍然不明白爲什麼它是必要的,也許我需要重新研究虛擬繼承。 – vincent

+0

只有當您通過多重繼承多次繼承共同祖先時才需要虛擬繼承。 – cababunga

回答

3

這是怎麼回事

當你繼承實際上,你是問你繼承的類型是「單身」的對象heiarchy內(而不是計劃範圍的單例,而是一個實例範圍內的單,看起來很奇怪)。

作爲副作用,實際上大多數派生類負責構建虛擬基類,而不管存在哪些中間類。

虛擬繼承的目的是當您需要從某個類多次繼承,但確保只存在一個實例時。 C++的設計者沒有制定出一套複雜的規則來確定哪個構造函數調用是被調用的,而是規定了派生最多的類(實際創建的類)決定了調用哪個構造函數。

其他人都可以參加,但只有在創建特定類別(而不是衍生更多的特定類別)的實際實例時纔會產生他們的意見。

你可能不希望virtual

virtual繼承幾乎無關virtual方法/成員函數。除非需要二進制佈局或鑽石繼承的原因,否則不要使用它。

簡單地把它從你的代碼中消除,如果你永遠不會沿着2個不同的路徑繼承同一個派生類,那麼你就不會錯過它。

class DerivedOne : public Base<std::string> 

class DerivedTwo : public DerivedOne 

和你的代碼編譯並執行你彷彿想。

+0

謝謝你,我開始變得非常沮喪。這清除了我對虛擬繼承的使用的明顯誤解! – vincent

+1

@vincent,是的,在這裏使用相同的關鍵字是相當不幸的。 – SergeyA

1

當您使用虛擬繼承它始終是最初派生類的初始化虛擬基地。即使你的中間類傳遞了一個值,它不會在其他派生類中這樣做,它必須直接初始化Base。由於你的大部分派生類(DerivedTwo)不直接初始化Base,所以Base沒有傳遞給它的任何參數就被初始化了,所以需要一個默認的構造函數。

class DerivedTwo : public virtual DerivedOne 
{ 
public: 
    DerivedTwo(const std::string& path) : Base(path), DerivedOne(path) {} 
} 

或者,停止使用虛擬繼承,只使用「正常」繼承。

class DerivedOne : public Base<std::string> 

當然除非,你真的需要虛擬繼承:也就是說,改變這種:

class DerivedOne : public virtual Base<std::string> 

了這一點。 :)