2010-01-03 85 views
4

我在寫一個模板,我試圖提供一個模板類的類專業化。當使用它實際上我與模板類的derivitives instanciating,所以我有這樣的事情:通過模板基類專門設計模板

template<typename T> struct Arg 
{ 
    static inline const size_t Size(const T* arg) { return sizeof(T); } 
    static inline const T*  Ptr (const T* arg) { return arg; } 
}; 

template<typename T> struct Arg<Wrap<T> > 
{ 
    static inline const size_t Size(const Wrap<T>* arg) { return sizeof(T); } 
    static inline const T*  Ptr (const Wrap<T>* arg) { return arg.Raw(); } 
}; 

class IntArg: public Wrap<int> 
{ 
    //some code 
} 

class FloatArg: public Wrap<float> 
{ 
    //some code 
} 
template<typename T> 
void UseArg(T argument) 
{ 
    SetValues(Arg<T>::Size(argument), Arg<T>::Ptr(&argument)); 
} 

UseArg(5); 
UseArg(IntArg()); 
UseArg(FloatArg()); 

在所有情況下的第一個版本被調用。所以基本上我的問題是:我在哪裏出錯了,我怎麼讓他調用在調用UseArg(5)時返回arg的版本,而在調用UseArg(intArg)時調用另一個呢?其他方式做這樣的事情(不改變UseArg的接口)當然歡迎。

作爲一個註釋,這個例子有點簡單,這意味着在實際的代碼中我包裝了一些更復雜的東西,派生類有一些實際的操作。

回答

4

我認爲有三個途徑:

1)專業生產精氨酸的派生類型:

template <typename T> struct Arg ... 
template <> struct Arg <IntArg> ... 
template <> struct Arg <FloatArg> ... 
// and so on ... 

這很糟糕,因爲你不能預先知道你會有什麼類型。當然,如果你有這些類型,你可以專門化,但這必須由實現這些類型的人來完成。

2)不提供默認的,專門爲基本類型

template <typename T> struct Arg; 
template <> struct Arg <int> ... 
template <> struct Arg <float> ... 
// and so on... 
template <typename T> struct Arg <Wrap<T> > ... 

這不是理想的太(取決於「基本類型」多少您希望使用)

3)使用IsDerivedFrom招

template<typename T> struct Wrap { typedef T type; }; 
class IntArg: public Wrap<int> {}; 
class FloatArg: public Wrap<float> {}; 

template<typename D> 
class IsDerivedFromWrap 
{ 
    class No { }; 
    class Yes { No no[3]; }; 

    template <typename T> 
    static Yes Test(Wrap<T>*); // not defined 
    static No Test(...); // not defined 

public: 
    enum { Is = sizeof(Test(static_cast<D*>(0))) == sizeof(Yes) }; 
}; 


template<typename T, bool DerivedFromWrap> struct ArgDerived; 

template<typename T> struct ArgDerived<T, false> 
{ 
    static inline const T* Ptr (const T* arg) 
    { 
     std::cout << "Arg::Ptr" << std::endl; 
     return arg; 
    } 
}; 

template<typename T> struct ArgDerived<T, true> 
{ 
    static inline const typename T::type* Ptr (const T* arg) 
    { 
     std::cout << "Arg<Wrap>::Ptr" << std::endl; 
     return 0; 
    } 
}; 

template<typename T> struct Arg : public ArgDerived<T, IsDerivedFromWrap<T>::Is> {}; 

template<typename T> 
void UseArg(T argument) 
{ 
    Arg<T>::Ptr(&argument); 
}; 

void test() 
{ 
    UseArg(5); 
    UseArg(IntArg()); 
    UseArg(FloatArg()); 
} 

調用測試()輸出(因爲據我所知,是你的目標):

Arg::Size 
Arg<Wrap>::Size 
Arg<Wrap>::Size 

擴展它可以與更多類型的工作就像裹是可能的,但太雜亂,但它的伎倆 - 你不需要做一堆專業領域。

有一兩件事值得一提的是,在我的代碼ArgDerived專業與 IntArg代替Wrap<int>,因此調用的IntArg代替Wrap<int>sizeof(T)ArgDerived<T, true>收益的大小,但可以將其更改爲sizeof(Wrap<T::type>),如果這是你的意圖。

-1

typedef Wrap<int> IntArg;而不是導出將解決您的問題。

事實上,編譯器不會搜索基類的專業化。考慮一個簡單的例子:

class A { }; 
class B: public A { }; 
template<class T> 
void f(T const &) 
{ std::cout << "T" << std::endl; } 
template<> 
void f<A>(A const&) 
{ std::cout << "A" << std::endl; } 

int main() 
{ f(A()); f(B()); } 

它打印「A T」。

+0

這可能會解決問題,但不是模板專業化。 – 2010-01-03 23:47:46

+0

它也不解決我的問題,因爲我真的需要派生類 – Grizzly 2010-01-03 23:57:47

+0

哈桑,那麼什麼是部分專業化? :) – 2010-01-04 00:06:48

0

正如Nicht指出的那樣,編譯器不會搜索任何基類的特化。

而不是專門的Arg類,爲什麼你不用過載 UseArg函數?有一個模板版本,需要一個T,並有一個重載的版本,需要一個包裝。重載版本可以根據需要「拆包」到原始指針。

0

而不是創造精氨酸這是你可以讓普通的類,然後過載大小和PTR靜態成員模板:

struct Arg 
{ 
    template<class T> 
    static const size_t Size(const T* arg) { return sizeof(T); } 
    template<class T> 
    static const size_t Size(const Wrap<T>* arg) { return sizeof(T); } 
    template<class T> 
    static const T*  Ptr (const T* arg) { return arg; } 
    template<class T> 
    static const T*  Ptr (const Wrap<T>* arg) { return arg->Raw(); } 
}; 

編輯: 嗯,它不會工作,我剛剛檢查...