2011-11-17 58 views
0

不太確定如何在此處標題。使用繼承而不是typedefs來實現正向聲明以及回調造成的複雜性

的問題是,我有一個類模板:

template<various parameters> struct Base { ... }; 

然後,我有一個開放式的集合,從它派生類:

struct A: public Base<some arguments> { ... }; 
struct B: public Base<other arguments> { ... }; 
struct C: public Base<different arguments> { ... }; 
... 

重要的是,派生類添加什麼除了修改模板參數之外 - 不是成員函數,特別是不是成員變量。所以他們的二進制表示應該都是一樣的。我使用繼承而不是typedefs的唯一原因是我希望能夠轉發聲明A,B,C等(或者更確切地說,A,B,C等由客戶使用提供的宏,我想,爲了方便起見,他們能夠正向聲明。)完全有A,B,C等的原因是爲了避免必須手工寫出模板參數時間。

問題是,Base有一些函數將回調函數作爲參數,並且回調函數應該將類本身作爲參數引用。

因此,我希望有回調的功能,如:

void foo (A&, ...); 
void bar (B&, ...); 

等等。並且在Base中實現回調函數時,我想將* this作爲參數傳遞給回調函數。非常明智的是,C++不允許我這樣做,因爲通常基類在預期的派生類中傳遞並不安全。但在這種特殊情況下,它不應該成爲一個問題,因爲派生類不向基礎添加任何內容,而只是用來代替typedef。

所以看起來我有三個標準:

  • 我想要的「類型定義」被向前申報。
  • 我需要能夠將基類傳遞給回調函數。
  • 我想能夠使用'typedefs'而不是基類來聲明回調。

它們互不相容。 (第三個標準同樣是爲了客戶的方便,因爲每次手動向基類寫出模板參數都會令人討厭地冗長,並且不想這樣做是因爲'typedefs 」)

我看到三個解決方案:

  • 跌落第一或第三繞圈。

  • 某種醜陋和複雜的方案,我檢查回調函數期望什麼類型的參數,並驗證它是基類的僞類型定義(通過檢查sizeof是否相同,並且由SFINAE導出從基地,或檢查由僞typedef生成的宏插入某種類型的令牌,或類似的東西),然後執行強制轉換。

有沒有更好的?

(C++ 11是允許的。)

+2

雞蛋裏挑骨頭,但重要的是:你是不是從「它」,即從模板派生。相反,你是從各種*不同的,無關的類*基類','基類'等等 –

+0

嗯,是的,我是從模板的各種實例化派生出來的,適用於各種參數。這就是我的意思。 – glaebhoerl

回答

4

採用CRTP idiom並通過派生類藏漢在模板的參數。對於回調,只需將*this投射到派生類型。

template<class Derived, other params...> 
struct Base{ 
    template<class F> 
    void do_callback_stuff(F func){ 
     func(static_cast<Derived&>(*this), ....); 
    } 
}; 

struct A : public Base<A, other args...>{}; 
+0

CRTP!當然。有一個明確答案的問題讓人耳目一新。 (雖然值得注意的是,這基本上是第三個更清潔和更有紀律的化身,「從上面數出來並且做一個演員」解決方案。) – glaebhoerl

+0

後續問題,因爲我仍然想對Derived進行一些靜態檢查。派生(Derived,Base)和sizeof(Derived)== sizeof(Base)是static_cast的一個充分條件(Base&)是否安全? – glaebhoerl

+0

@illissius:試試'std :: is_base_of :: value'。它在''中。這一點就夠了。如果你確實想確保'Derived'實際上是從'Base'派生的(應該是這種情況),那麼把一個簡單的static_assert放入類體中。 'template <...> class Base {typedef Base <...> this_type; static_assert(std :: is_base_of :: value,「錯誤的派生模板參數!」); };' – Xeo

1

也許奇異遞歸模板模式和隱式轉換的組合會做的伎倆:

template<typename Derived, various parameters> struct Base 
{ 
    operator Derived&() { return static_cast<Derived&>(*this); } 
    operator Derived const&() const { return static_cast<Derived const&>(*this); } 
    other stuff; 
}; 

struct A: Base<A, some arguments> {}; 
2

正如其他人則建議,CRTP可能是一個解決方案。

但是,您還應該重新考慮何時以及爲什麼回調需要引用對象的專用調用方不詳細瞭解自己的對象。我想說:「在實例化之前,類模板對自己的細節知之甚少」。

因此,檢查回調可能具有的使用模式,與調用者對象引用(實例化的類模板對象)有關。並提取一個通用的基類或基本接口。

然後獲得從基類的模板:

struct BaseInterface { 

    // HERE, declare anything, that callbacks may use 

    virtual void foo() = 0; 
}; 

template<various parameters> 
struct Base : public BaseInterface { ... }; 


void some_callback(BaseInterface& caller, ...); 
+0

靈感來源的思想。但即使以這種方式限制接口是有意義的(但事實並非如此,但你不知道),BaseInterface仍然需要以相同的方式進行模板化,而我們又回到了原點。 – glaebhoerl