2010-01-03 80 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(); } 
}; 

编辑: 嗯,它不会工作,我刚刚检查...