通过模板基类专门化模板

时间:2010-01-03 23:06:21

标签: c++ inheritance templates specialization

我正在编写一个模板,我正在尝试为类本身提供专门化,而这个类本身就是一个模板类。在使用它时,我实际上是使用模板化类的派生来实现它,所以我有这样的东西:

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 个答案:

答案 0 :(得分:4)

我认为有三种方法:

1)为派生类型专门化Arg:

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());
}

调用test()输出(据我所知,这是你的目标):

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

扩展它以使用更多类型如Wrap是可能的,但也很麻烦,但它可以解决问题 - 你不需要做一堆专业化。

值得一提的是,我的代码ArgDerived专门用于 IntArg代替Wrap<int>,因此在sizeof(T)中调用ArgDerived<T, true>会返回IntArg而不是Wrap<int>的大小,但您可以将其更改为{{ 1}}如果这是你的意图。

答案 1 :(得分:0)

正如Nicht所指出的,编译器不会搜索任何基类的特化。

为什么不重载 UseArg函数而不是专门化Arg类?有一个带有T的模板版本,并且有一个带有Wrap的重载版本。重载版本可以根据需要“解包”到原始指针。

答案 2 :(得分:0)

不是创建作为模板的Arg,而是可以使它成为常规类,然后OVERLOAD Size和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(); }
};

编辑: 好吧,它不会起作用,我刚刚检查过......

答案 3 :(得分:-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”。