简体   繁体   English

通过模板基类专门化模板

[英]Specializing a template by a template base class

I'm writing a template for which I'm trying to provide a specialization on a class which itself is a template class. 我正在编写一个模板,我正在尝试为类本身提供专门化,而这个类本身就是一个模板类。 When using it I'm actually instanciating it with derivitives of the templated class, so I have something like this: 在使用它时,我实际上是使用模板化类的派生来实现它,所以我有这样的东西:

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

In all cases the first version is called. 在所有情况下,都会调用第一个版本。 So basically my question is: Where did I went wrong and how do I make him call the the version which returns arg when calling UseArg(5), but the other one when calling UseArg(intArg)? 所以基本上我的问题是:我哪里出错了,如何调用UseArg(5)时调用返回arg的版本,而调用UseArg(intArg)时调用另一个版本? Other ways to do something like this (without changing the interface of UseArg) are of course welcome to. 其他方式做这样的事情(不改变UseArg的界面)当然是受欢迎的。

As a note the example is a little simplyified meaning that in the actual code I'm wrapping some more complex things and the derived class has some actual operations. 作为一个注释,这个例子有点简单,意思是在实际的代码中我包装了一些更复杂的东西,派生类有一些实际的操作。

I think there are three approaches: 我认为有三种方法:

1) Specialize Arg for derived types: 1)为派生类型专门化Arg:

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

This sucks, because you can't know in advance what types you will have. 这很糟糕,因为你事先无法知道你将拥有什么类型。 Of course you you can specialize once you have these types, but this has to be done by someone who implements these types. 当然,一旦你拥有这些类型,你就可以专攻,但这必须由实现这些类型的人来完成。

2) Do not provide default one and specialize for basic types 2)不提供默认值并专门用于基本类型

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

It's not ideal too (depends on how many "basic types" you expect to use) 它也不理想(取决于你期望使用多少“基本类型”)

3) Use IsDerivedFrom trick 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());
}

Calling test() outputs (as I understand that's your goal): 调用test()输出(据我所知,这是你的目标):

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

Extending it to work with more types like Wrap is possible, but messy too, but it does the trick - you don't need to do a bunch of specializations. 将它扩展为使用更多类型(如Wrap)是可能的,但也很麻烦,但它可以解决问题 - 您不需要做一堆专业化。

One thing worth mentioning is that in my code ArgDerived is specialized with IntArg instead of Wrap<int> , so calling sizeof(T) in ArgDerived<T, true> returns size of IntArg instead of Wrap<int> , but you can change it to sizeof(Wrap<T::type>) if that was your intention. 值得一提的是,在我的代码中, ArgDerived专门用于IntArg而不是Wrap<int> ,因此在ArgDerived<T, true>调用sizeof(T)返回IntArg大小而不是Wrap<int> ,但您可以更改它to sizeof(Wrap<T::type>)如果这是你的意图。

As Nicht pointed out, the compiler doesn't search for specializations for any base classes. 正如Nicht指出的那样,编译器不会搜索任何基类的特化。

Instead of specializing the Arg class, why don't you overload the UseArg function? 而不是专门化Arg类,为什么不重载 UseArg函数? Have a template version which takes a T, and have an overloaded version which takes a Wrap. 有一个带有T的模板版本,并且有一个带有Wrap的重载版本。 The overloaded version can "unwrap" to the raw pointer as needed. 重载版本可以根据需要“解包”到原始指针。

Instead of creating Arg which is a template you may make it regular class and then OVERLOAD Size and Ptr static members: 您可以将其作为常规类,然后OVERLOAD Size和Ptr静态成员,而不是创建作为模板的Arg:

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

EDIT: Well, it won't work, I've just checked... 编辑:嗯,它不会工作,我刚刚检查过......

typedef Wrap<int> IntArg; instead of deriving will solve your problem. 而不是派生将解决您的问题。

In fact the compiler doesn't search for specializations with base classes. 实际上,编译器不会搜索具有基类的特化。 Consider a simple example: 考虑一个简单的例子:

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

It prints "AT". 它打印“AT”。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM