由於我的問題聽起來很混亂,我認爲最好清楚說明我想達到的目標。派生類中的C++成員函數模板,如何從基類中調用它?
我有(忽略繼承現在,專注於X):
class Base {};
class X : public Base {
private:
double m_double;
public:
template<class A> friend
void state(A& a, const X& x) {
data(a, x.m_double, "m_double");
}
};
我現在可以將任意新的類,基於該數據功能是如何超載,我們將把這些不同的行動作爲訪問者在以下幾點:
class XmlArchive {...}; //One Accessor
template<class T>
void data(XmlArchive& a, const T& t, const std::string& str) {
//read data and serialize it to xml Archive
}
class ParameterList {...}; //Another Accessor
template<class T>
void data(ParameterList& a, const T& t, const std::string& str) {
//put data in a parameter list
}
然後我就可以寫:
X myX;
XmlArchive myArchive;
state(myArchive, myX);
ParameterList myParameters;
state(myParameters, myX);
奇妙的是,代碼重用! :D然而,以下(明顯)失敗:
Base* basePtr = new X; //This would come from factory really, I should not know the type X
state(myParameters, *basePtr); //Error
目標是使最後一次呼叫成功。我已經考慮(以及爲什麼它是不能接受的):
第一種選擇:讓所有訪問者從一個公共基類繼承,說AccessorBase,然後在基地
寫virtual state(AccessorBase& a) const = 0;
和實施所需的代碼X(調用狀態的語法稍有不同,但可以修復)。 問題是,AccessorBase將需要爲每個可能的類型提供一個虛擬函數,它將作爲數據函數中的第二個參數。由於這些類型可以是用戶定義的類(請參閱類構成的情況,X中有Y作爲數據成員),但我不明白如何使此策略可行。
第二個選項:在每個訪問器的Base中創建一個虛函數。這違反了開放/關閉原則,因爲添加一個新的Accessor類(比如TxtArchive)將需要修改基類。
我明白爲什麼虛擬成員函數不能被模板化(可能在不同的編譯單元中有不同的vtbls)。 但是,它似乎應該有解決這個問題的方法...... Base知道它確實是X類型的,Accessor的類型總是顯式的,所以它是尋找調用方法的問題(對於XmlArchive類型的存取器):
state(xmlArchive, x); //xmlArchive of type XmlArchive, x of type X
這將產生結果。
綜上所述,起來,我想電話:
state(myParameters, *basePtr);
成功,如果basePtr指向一個派生類與呼叫兼容的功能模板,並在其他方面拋出異常。我發現boost :: serialize做了類似的事情,但我無法弄清楚它是如何的(它可能是通過模板在C++中重新實現繼承關係,我看到一個This()函數返回了最多的派生指針但它變得非常混亂......)
再次感謝您的幫助!
但正如我在後文中所述,我知道我知道我有X,因此調用正確函數的責任應該委託給派生類本身(XY和Z或任何其他從Base繼承的),它知道關於它的自我類型。 – stepelu 2010-05-26 16:34:14
是的,這就是爲什麼我說你需要RTTI(或者存儲類型X,Y,Z的Base中的一些枚舉字段),因爲你需要根據對象的_runtime_類型採用不同的分支。你不能使用虛擬函數,這隻能讓你使用某種形式的RTTI,或者可能用仿函數來模擬虛擬函數。無論哪種方式,您或編譯器都必須將某些內容添加到Base。 – Eloff 2010-05-26 20:50:22