2010-07-15 120 views
17

我有如下代碼。我有一個抽象模板類Foo和兩個從模板實例派生的子類(Foo1和Foo2)。我希望在我的程序中使用可以指向Foo1或Foo2類型的對象的指針,因此我創建了一個IFoo接口。在C++中爲抽象類模板創建一個接口

我的問題是我不知道如何在接口中包含functionB,因爲它依賴於模板實例化。是否有可能通過接口使功能B可訪問,或者我試圖做到不可能?

非常感謝您的幫助。

class IFoo { 
    public: 
     virtual functionA()=0; 

}; 

template<class T> 
class Foo : public IFoo{ 
    public: 
     functionA(){ do something; }; 
     functionB(T arg){ do something; }; 
}; 

class Foo1 : public Foo<int>{ 
... 
}; 

class Foo2 : public Foo<double>{ 
... 
}; 

回答

0

我不認爲你可以得到你想要的。如果你想實現你的建議,可以考慮這個:如果你有一個指向IFoo實例的指針,並且你打電話給functionB(),你應該給它什麼類型的參數?根本的問題是Foo1::functionBFoo2::functionB有不同的簽名並做不同的事情。

+0

但在技術上,我不會要創建的IFoo的一個實例,我只希望創建一個Foo1(或2)(由IFoo指針指向)的實例,並且我認爲可以通過動態邊界找到適當的函數? – bishboshbash 2010-07-15 00:34:06

+0

@bishboshbash:對於通過動態綁定找到的函數,必須知道所有的參數類型;否則,如果使用重載函數,則不清楚要查找什麼。 – liori 2010-07-15 00:38:10

+0

我想我明白了。如果我創建了Foo1的實例並且調用了Foo1.functionB(..),它會正常工作,因爲模板已經被實例化。 我可以通過多重繼承創建一個抽象類來指向類型爲Foo1或Foo2的對象,因此可以避免動態綁定嘗試通過一個未被實例化的模板的問題? – bishboshbash 2010-07-15 00:51:24

4

最簡單的方法是使您的界面模板化。

template <class T> 
class IFoo { 
    public: 
     virtual void functionA()=0; 
     virtual void functionB(T arg){ do something; }; 
}; 

template<class T> 
class Foo : public IFoo<T>{ 
    public: 
     void functionA(){ do something; }; 
     void functionB(T arg){ do something; }; 
}; 
+2

這意味着我無法創建IFoo類型的指針,並讓它們指向Foo1或Foo2的實例化。 – bishboshbash 2010-07-15 00:39:19

+2

這是正確的,這是一個基本問題。 functionB取決於實例化的模板類型,**必須在編譯時知道。 – 2010-07-15 01:01:18

+0

@bishboshbash您的要求首先是不合格的。 Foo1和Foo2具有不同的基類(IFoo 和IFoo 是兩種不同的類型)。 – h9uest 2015-01-08 15:00:28

4

由於functionB的參數類型必須事先知道,你只有一個選擇:使它成爲一個類型,可容納每一個可能的論點。這有時被稱爲「頂級類型」,並且增強庫的類型與頂級類型的功能非常接近。這裏是可以工作:

#include <boost/any.hpp> 
#include <iostream> 
using namespace boost; 

class IFoo { 
    public: 
    virtual void functionA()=0; 
    virtual void functionB(any arg)=0; //<-can hold almost everything 
}; 

template<class T> 
class Foo : public IFoo{ 
    public: 
     void functionA(){ }; 
     void real_functionB(T arg) 
     { 
     std::cout << arg << std::endl; 
     }; 
     // call the real functionB with the actual value in arg 
     // if there is no T in arg, an exception is thrown! 

     virtual void functionB(any arg) 
     { 
      real_functionB(any_cast<T>(arg)); 
     } 
}; 

int main() 
{ 
    Foo<int> f_int; 
    IFoo &if_int=f_int; 

    if_int.functionB(10); 

    Foo<double> f_double; 
    IFoo &if_double=f_double; 
if_int.functionB(10.0); 

} 

不幸的是,any_cast不知道平時的轉換。例如any_cast<double>(any(123))會引發異常,因爲它甚至不會嘗試將整數123轉換爲double。如果不關心轉換,因爲無論如何都無法複製所有轉換。所以存在一些限制,但如有必要,可以找到解決方法。

+1

要使用這個答案,調用者('main()')必須知道'IFoo'實例的實際子類型('if_int'和'if_double'),以便它可以傳遞正確的參數。如果它錯了,就會拋出運行時異常!那麼,爲什麼不讓這個調用只使用'dynamic_cast'? – Karmastan 2010-07-15 01:42:51

+1

如果我有調用者使用dynamic_cast,那麼我需要他了解Foo ,Foo 。但是抽象接口的好處是,我不需要告訴任何人,接口是如何實現的。它可以是來自共享庫的私人第三方類型,也可以是函數體中的本地類型。 此外,它只是boost的any_cast的限制,它引發了錯誤的參數類型。您可以自由地增強其功能,例如爲內在類型進行正確的轉換。但是,無法完成的是抓住所有**有效轉換。 – 2010-07-15 09:22:46

9

你實際上正在嘗試不可能的事情。

問題的核心很簡單:virtualtemplate混合不好。

  • template是關於編譯時代碼的生成。你可以把它看作是某種類型感知的宏+爲元編程提供了一些細小的技巧。
  • virtual是關於運行時決策,這需要一些工作。

virtual通常使用虛擬表(想象一個列出方法的表)。在編譯時需要知道方法的數量,並在基類中定義。

但是,根據您的要求,我們需要一個無限大小的虛擬表格,其中包含我們尚未見過的類型的方法,並且只會在未來幾年中定義......這是不可能的。

如果有可能?

那麼,它只是沒有意義。當我撥Foo2int時會發生什麼?這不是爲了它!因此打破了Foo2實施IFoo的所有方法的原則。

所以,這將是更好,如果你說真正的問題,這樣一來,我們可以幫你在設計層面,而不是在技術層面:)

+0

我必須上課Foo1和Foo2。它們都包含相似的數據結構:一個包含整數,另一個包含有理數(整數對)。它們有許多方法是相同的(相對於int/int對),它們本身與其他(int/pair對)數據類型交互。但是他們有幾種方法的實施方式不同。 我最初是通過繼承來實現它的,但是這些數據結構是在子類中定義的,所以我不能在基類中寫入作用於它們的成員函數,這會導致大量的代碼重複。 – bishboshbash 2010-07-18 12:23:03

+0

我有很多次相同的問題,並在其接口(10+)中包含許多方法和屬性的類。我所做的是爲每個實例創建接口,並使用閉包來減少方法的數量,例如'ErrorCode ScalarParameterSet(ParamType t,float ParamValue)'用於聯合所有設置器。如果你願意,我可以舉一個例子 – 2016-08-19 15:07:38