2017-08-03 60 views
4

對於一個示範的緣故想象,我有一些動物類,每一類由「動物」類派生的,每個「知道」,他們是什麼類型的,並且每個具有某種獨特的能力:在C++接口中聲明模板函數?

enum class animal_type { antelope, bear, cat }; 

class animal 
{ 
}; 

class antelope : public animal 
{ 
public: 
    static const animal_type type = animal_type::antelope; 
    void run() { std::cout << "antelope runs\n"; }; 
}; 

class bear : public animal 
{ 
public: 
    static const animal_type type = animal_type::bear; 
    void roar() { std::cout << "bear roars\n"; }; 
}; 

class cat : public animal 
{ 
public: 
    static const animal_type type = animal_type::cat; 
    void meow() { std::cout << "cat meows\n"; }; 
}; 

現在,我想根據自己的類型,能夠檢索動物:

class animal_getter 
{ 
public: 
    animal& get(animal_type t) 
    { 
     static antelope s_antelope; 
     static bear s_bear; 
     static cat s_cat; 

     switch (t) 
     { 
      case animal_type::antelope: 
       return s_antelope; 

      case animal_type::bear: 
       return s_bear; 

      case animal_type::cat: 
       return s_cat; 
     } 
    } 
}; 

最後,這將是很好得到的實際類型動物的背部,使調用語法更好:

template<typename T> 
T& get() 
{ 
    return static_cast<T&>(get(T::type)); 
} 
現在

我可以寫這樣的事:

animal_getter ag; 
ag.get<antelope>().run(); 

而非wordier:

animal_getter ag; 
static_cast<antelope&>(ag.get(animal_type::antelope)).run(); 

我希望沒有什麼太不合理有關。但是現在我想能夠單元測試獲取動物,所以理想情況下可以僞造animal_getter類(假設實際的實現訪問數據庫或者單元測試中不需要的東西,因此是假的) 。所以最好定義一個「動物吸氣」類的接口,然後創建一個實現該接口的假。這裏的問題,可以寫這個接口?這是不會編譯:

struct IAnimalGetter 
{ 
    virtual template<typename T> T& get() = 0; 
}; 

是否有可能挽救這個想法或者模板函數永遠不會被宣佈爲虛擬定義,包括用戶的界面的目的是什麼?

如果這個想法不是首發,那麼在什麼時候開始出問題了?當模板函數被寫入了它自己的模型嗎?我是否應該停止返回一個動物對象,然後讓調用者對演員負責?

+0

這是不是很清楚你爲什麼需要這個。公開你的靜態數據成員。 'ag.get ().run()'什麼也不做'ag.s_antelope()。run()'不。 –

+0

(續)沒有虛擬模板,所以虛擬'get template'是不可能的。你可以通過訪問者模式和/或ctrategicalky放置演員來解決這個問題,但是你仍然不清楚你想要達到什麼目的。什麼'得到()'買你'get_antelope()'不? –

+1

如果派生類沒有任何共同點,那麼也許錯誤是有一個空的基類?需要投射或打開類型都是模型出現問題的跡象。肯定羚羊不沉默,熊可以跑。 –

回答

0

虛擬模板功能是不允許的,但你的模板函數沒有邏輯,所以在這種情況下,我會用:

struct IAnimalGetter 
{ 
    virtual animal& get(animal_type t) = 0; 

    template<typename T> 
    T& get() 
    { 
     return static_cast<T&>(get(T::type)); 
    } 
}; 

和:

class animal_getter : public IAnimalGetter 
{ 
public: 
    animal& get(animal_type t) 
    { 
     // implementation 
    } 
}; 

class mock_animal_getter : public IAnimalGetter 
{ 
public: 
    animal& get(animal_type t) 
    { 
     // mock implementation 
    } 
}; 
+0

我曾考慮過這個問題,但有一點讓「界面」不包含純虛函數以外的東西。很高興看到有人將此視爲合法行爲。謝謝。 – WalderFrey

+0

另一種選擇是提供非會員模板功能。這將保持你的界面純粹,但會使你的代碼更少面向對象。 – Oleg

0

使用鑄造那種失敗的這裏使用模板的要點。 對於我來說,它並不是很清楚吸氣劑類的用途。它清楚地說明了它保存一些預定義類型的靜態實例,但不清楚爲什麼它的一個類處理所有類型。我看不到你獲得太多,因爲它沒有太多的代碼,你必須在通話中指定類型。 switch語句是一個潛在的錯誤,如果添加一個新的動物類型,它仍然會編譯,但新類型不會在交換機中處理,並且會出現運行時錯誤。

如果您不需要靜態實例,只需構建正確的類型即可。

如果你確實需要它們,那麼考慮它們是否應該由該類型管理(即熊有一個方法來獲得熊實例)。另一個選項(正如已經指出的那樣)是暴露你的getter持有的每種類型的靜態實例。

如果你不想管理靜態實例在相關的類時,請考慮單獨干將:

template<typename T> 
struct IGetter 
{ 
    virtual T Get() =0; 
}; 

template<typename T> 
struct RealGetter : public IGetter<T> 
{ 
    static T item; 
    virtual T Get() 
    { 
     return item; 
    } 
}; 

template<typename T> 
struct MockGetter : public IGetter<T> 
{ 

    virtual T Get() 
    { 
    // some mock implementation 
    } 
}; 
+0

請不要在示例代碼中插入太多內容 - 它是爲了演示原理,而不是被挑選出來。實際問題以粗體突出顯示 - 這就是我真正想問的問題,將代碼用作演示我面臨的問題的工具。在我正在處理的真實代碼當中,沒有靜態實例被單個類保存 - 它們實際上是從內存數據庫中獲取的業務對象。這裏的代碼爲了演示目的而被剝離,並不意味着現實。 – WalderFrey

+0

我明白這是簡化的代碼,但我認爲你的getter已經被簡化了,所以它隱藏了你的意圖。你的標題和粗體問題仍然相當普遍,所以我認爲一個涉及不同類結構的答案(沒有投射)可能是最好的選擇,但你想要的東西很難從你的例子中分辨出來。每個gettable類的模板有沒有理由不符合您的實際需求? – ROX

+0

我正在使用遺留代碼。我們有一個單一的類,它充當通用業務對象的入口。你要一個嗎?使用巨石提供的功能(有點像我的例子中的animal_getter)!代碼庫是古老的,不會將這種功能轉移到各種類型(熊,貓,蟑螂)中。模板get函數是讓調用代碼更清晰(不投射)的折衷方案,但它使得界面的定義變得棘手。因此,這個問題。希望有所幫助。 – WalderFrey

1

模板和管型和類型的變量似乎並沒有什麼用處的聲明用例(獲取動物,然後立即利用其獨特的特徵)。

antelope& get_antelope(); 
bear& get_bear(); 
cat& get_cat(); 

這就是所有需要的。

get_antelope().run(); 
get_cat().meow(); 

這些函數可以是獨立的或成員的,如果需要,可以是虛擬的。每個返回類型需要一個函數。這與每個返回類型的switch語句中的一種情況沒有什麼不同,並且在某些方面優於它。

如果出於某些尚未明確的原因需要函數模板,則可以基於普通函數定義一個函數模板。

template <class A> 
A& get_animal(); 

template<> antelope& get_animal<antelope>() { return get_antelope(); } 
template<> bear& get_animal<bear>() { return get_bear(); } 
template<> cat& get_animal<cat>() { return get_cat(); } 
+0

我的示例代碼在事後看來並不是很有說服力。 get函數的主體應該只是一個註釋:「//以某種方式獲取動物」。我的現實是沒有開關。每個版本都添加了新類型,每次添加get_new_animal_type都將是一個容易出錯和不必要的負擔。 – WalderFrey

+0

@WalderFrey我已經回答了你所問的問題。如果您有不同的問題,請單獨提出。 –

+0

我並沒有問另外一個問題。對不起,如果評論似乎是一個新的問題。我只是想解釋爲什麼你的回答在我看來不合適。我在這方面是新的:或許倒票比評論更好?無論如何,感謝您花時間回答,我很感激。 – WalderFrey