2012-11-09 163 views
2

我有以下結構的類和模板化包裝類。帶基本類型實例化模板參數的C++函數

class A {}; 
class B : public A {}; 

template<class T> 
class Foo { 
    T val; 
public: 
    Foo(T t) {val = t}; 
    T get() {return val}; 
} 

現在,我需要一個函數來處理存儲在模板中的值,但我無法修改模板本身。我想是簽名類似於

void process(Foo<A>*) {/* do something */} 

的功能,我想使用它像

B b; Foo<B> foo(b); 
process(&foo); 

但是,這是行不通的,因爲編譯器不知道如何轉換Foo<B>*Foo<A>* 。是否有任何workarround修復過程功能?

回答

2

製作process函數模板:

template <typename T> void process(Foo<T>*) 

裏面的函數模板,你可以使用類型名T,如果它是A類。此功能模板將適用於Foo<B>的所有呼叫。

+0

使'進程'模板函數是我想要避免的。但是,我似乎是對我的問題最合理的解決方案。謝謝。 – cjbrehmer

0

雖然B是一種A,Foo<B>是不是一種Foo<A>

蘋果是一種水果。一袋蘋果也是一袋水果嗎?不,因爲你可以把梨放在一袋水果中。

在你的情況下,你不能在包裝器中創建任何東西,所以這個參數不起作用。但是C++編譯器不夠聰明,無法解決這個問題。在其他語言如Scala中,您實際上可以完成這項工作。

您可以製作process模板,但有時您不能使用,例如這是某個班的虛擬功能。

通過一些簡單到中等的手動工作和模板元編程,實際上可以使Foo<Derived>Foo<Base>一樣工作。手動工作將涉及將Foo拆分爲接口和實現部分。

// Instead of class B : public A, use class B : public DiscoverableBase<A> 
// Doesn't cost you anything. Won't work with multiple inheritance, where 
// scattered-inheritance or similar technique must be adopted. 

#include <cstdlib> 
template <typename B> 
struct DiscoverableBase : public B 
{ 
    typedef B Base; 
}; 

struct Void {}; 

// This fetches Base member type out of T, or Void if there's none. 
// Probably could be simplified a bit. 
template <typename T> 
class BaseOf 
{ 
    typedef char yes; 
    typedef struct { char a[2]; } no; 

    template <typename Q, size_t> struct GetBase; 

    template <typename Q> 
    struct GetBase<Q, sizeof(no)> 
    { 
     typedef Void Base; 
    }; 

    template <typename Q> 
    struct GetBase<Q, sizeof(yes)> 
    { 
     typedef typename T::Base Base; 
    }; 

    template <typename C> static yes check(typename C::Base*) ; 
    template <typename C> static no check(...); 
    public: 
    typedef typename GetBase<T, sizeof(check<T>(0))>::Base Base; 
}; 

// This is how you use DiscoverableBase. 
class A {}; 
class B : public DiscoverableBase<A> {}; 

// Foo is a bit more complicated now. 
template<typename T> class Foo; 

// The base case, inherited by Foo<X> where X has no discoverable base 
template<> class Foo<Void> {}; 

// This is the Foo interface. Note how it retuns a reference to T now. 
template<typename T> 
class Foo : public Foo<typename BaseOf<T>::Base> 
{ 
    public: 
    virtual T& get() = 0; 
}; 

// This is the Foo implementation. 
template<typename T> 
class FooImpl : public Foo<T> 
{ 
    T val; 
    public: 
    FooImpl(T t) {val = t;} 
    T& get() {return val;} 
}; 

// the rest is mostly unchanged 
void process(Foo<A>*) {/* do something */} 

int main() 
{ 
    B b; FooImpl<B> foo(b); 
    process(&foo); 
} 
+0

非常感謝您的解決方案。模板元編程的這種應用非常有見識。不幸的是,所提出的解決方案需要對代碼進行大量更改。因此,我會採取「流程」模板。 – cjbrehmer