【问题标题】:Specializing a template by a template base class通过模板基类专门化模板
【发布时间】:2010-01-03 23:06:20
【问题描述】:

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

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 的接口)。

作为一个注释,这个例子有点简单化,意思是在实际代码中我包装了一些更复杂的东西,派生类有一些实际操作。

【问题讨论】:

    标签: c++ inheritance templates specialization


    【解决方案1】:

    我认为有三种方法:

    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&lt;int&gt;,因此在ArgDerived&lt;T, true&gt; 中调用sizeof(T) 会返回IntArg 的大小而不是Wrap&lt;int&gt;,但如果这是您的意图,您可以将其更改为sizeof(Wrap&lt;T::type&gt;)

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        【解决方案3】:

        除了创建作为模板的 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(); }
        };
        

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

        【讨论】:

          【解决方案4】:

          typedef Wrap&lt;int&gt; 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”。

          【讨论】:

          • 这可能会解决问题,但不是模板专业化。
          • 它也不能解决我的问题,因为我真的需要派生类
          • Hassan,那什么是偏特化? :)
          • cprogramming.com/tutorial/template_specialization.html 。这是为了正确表示 STL 必须覆盖 C 中的极端情况——例如,迭代器。
          • 好的,template&lt;class T&gt; class Z{}; template&lt;class T&gt; class Z&lt;T*&gt; {} 是部分特化吗?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-10-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多