在C++泛型编程中如何只特化类的一个成员函数

在C++泛型编程中如何只特化类的一个成员函数

我们知道在C++模板编程中如果我们特化或是偏特化某个模板类, 我们需要重写整个模板类中的所有函数, 但是这些代码通常是非常相似的, 甚至在某些情况下可能只有一两个函数会不一样,其他函数都是一样的。在这种情况下,同时存在多份相同的代码,对我们维护这些代码是非常不利的, 我们最好只需要特化其中不一样的那个函数。

比如下面这个模板类:

 

template<typename T, unsigned B>
struct  Base
{
     // other function
     // ....
     void  Func(){ cout <<  " primary function "  << endl; }
};
void  test1()
{
    Base< int 1 > a;
    a.Func();
    Base< int 16 > b;
    b.Func();
}
int  main()
{
     test1();
}

 

 

只有当B等于16时, Func这个函数需要特化, 但是其他函数无论什么情况下都是一样的。

下面是我们的一些可能解决方案:

方法1:

 

template<typename T>
struct  Base<T,  16 >
{
     // other function
     // ....
     void  Func(){ cout <<  " specialization function "  << endl; }
};

 

点评:通过偏特化实现,需要重写所有的类成员方法。

 

方法2:

 

template<typename T, unsigned B>
struct  Base
{
     // other function
     // ....
     void  Func()
    {
         if (B ==  16 )
        {
            cout <<  " primary function "  << endl;
        }
         else
        {
            cout <<  " specialization function "  << endl;
        }
    }
};

 

点评: 通过运行时判断, 容易理解,但是相对低效。

 

方法3:

 

template<typename T, unsigned B>
struct  Base
{
     // other function
     // ....
     void  Func()
    {
#if  B!=16
            cout <<  " primary function "  << endl;
#else
            cout <<  " specialization function "  << endl;
#endif
    }
};

 

点评: 试图通过预编译来实现,但是这个方法是错误的。C++模板编译包括预编译,语法检查,模板实例化等阶段,在预编译阶段模板参数都还没有实例化呢。

 

方法4:

 

template<typename T, unsigned B>
struct  Base
{
     // other function
     // ....
    template<unsigned S>
     struct  FuncObj
    {
         void   operator ()()
        {
            cout<< " primary function " <<endl;
        }
    };
    template<>
     struct  FuncObj< 16 >
    {
         void   operator ()()
        {
            cout<< " specialization function " <<endl;
        }
    };
    FuncObj<B> Func;
};

 

点评: 通过成员类以防函数的形式特化, 增加了类成员变量。

 

方法5:

 

template<typename T, unsigned B>
struct  Base
{
     // other function
     // ....
    template<unsigned N>
     void  FuncImpl()
    {
        cout<< " primary function " <<endl;
    }
    template<>
     void  FuncImpl< 16 >()
    {
        cout<< " specialization function " <<endl;
    }
     void  Func()
    {
        FuncImpl<B>();
    }
};

 

点评:通过类成员模板函数特化来实现。

 

方法6:

 

template<typename T, unsigned B>
struct  Base
{
     // other function
     // ....
    template<unsigned N> 
     class  Int2Type
    {
         enum  { value = N };
    };
    template<unsigned V>
     void  FuncImpl( const  Int2Type<V>)
    {
        cout<< " primary function " <<endl;
    }
     void  FuncImpl( const  Int2Type< 16 >)
    {
        cout<< " specialization function " <<endl;
    }
     void  Func()
    {
        FuncImpl(Int2Type<B>());
    }
};

 

点评: 通过将int根据值的不同转成不同的类型,然后通过函数重载实现。

 

方法7:

 

namespace
{
    template < bool ,typename T,typename>  struct  conditional { typedef T type; };
    template <typename T,typename U>  struct  conditional< false ,T,U> {typedef U type; };
}
template< class  T, unsigned B>
struct  Base
{
     // other function
     // ....

     void  Func ()
    {
        typedef typename ::conditional<B!= 16 ,primary_t,spec_t>::type type;
        Func_impl(type());
    }
private :
     struct  primary_t { };
     struct  spec_t    { };
     void  Func_impl (primary_t) { std::cout <<  " primary function "  << std::endl; }
     void  Func_impl (spec_t   ) { std::cout <<  " specialization function "  << std::endl; }
};

 

点评: 和方法6类似,通过函数重载实现

 

方法8:

 namespace

 {
    template < bool ,typename T =  void struct  enable_if { typedef T type; };
    template <typename T>  struct  enable_if< true ,T> {};
}
template< class  T, unsigned B>
struct  Base
{
     // other function
     // ....

    template <unsigned N>
    typename ::enable_if< 16 !=N>::type
        FuncImpl () { std::cout <<  " primary function "  << std::endl; }
    template <unsigned N>
    typename ::enable_if< 16 ==N>::type
        FuncImpl () { std::cout <<  " specialization function "  << std::endl; }
     void  Func() {
        FuncImpl<B>();
    }
};

点评:通过enable_if, 利用SFINAE实现。

 

我们可以看到根据编译时模板参数int值的不同,我们重写模板类的某个成员函数的方法是多种多样的。针对上面这种情况,个人其实最推荐方法2,我们没必要把简单的问题复杂化。

 

下面我们考虑另外一个需求, 当模板类的某个参数是某种类型时, 我们要求特化其中的一个成员函数:

 

template<typename T1, typename T2>
struct  Base
{
     // other function
     // ....
     void  Func(){ cout <<  " primary function "  << endl; }
};
void  test2()
{
    Base< int int > a;
    a.Func();
    Base< int string > b;
    b.Func();
}
int  main()
{
    test2();
}

 

要求上面的模板类如果T2 是string类型, 我们要求对Func特殊重写,其他的成员函数无论什么情况实现都是一样的。

有了上面的那个例子的实现经验, 对这个问题我们解决就方便多了。

 

方法1:

 

template<typename T1, typename T2>
struct  Base
{
     // other function
     // ....
     void  Func()
    {
         if (typeid(std:: string ) == typeid(T2))
        {
            cout<< " specialization function " <<endl;
        }
         else
        {
            cout <<  " primary function "  << endl; 
        }
    }
};

 

点评:通过运行时类型识别(RTTI)实现,需要打开相关编译选项,并且低效。

 

方法2:

 

template<typename T1, typename T2>
struct  Base
{
     // other function
     // ....
    template<typename T>
     void  FuncImpl()
    {
        cout <<  " primary function "  << endl; 
    }
    template<>
     void  FuncImpl< string >()
    {
        cout <<  " specialization function "  << endl; 
    }
     void  Func()
    {
        FuncImpl<T2>();
    }
};

 

点评:通过成员函数特化实现

 

方法3:

 

template<typename T1, typename T2>
struct  Base
{
     // other function
     // ....
    template<typename T> 
     class  Type2Type
    {
        typedef T type;
    };
    template<typename T>
     void  FunImpl( const  Type2Type<T>)
    {
        cout <<  " primary function "  << endl; 
    }
    template<typename T>
     void  FunImpl( const  Type2Type< string >)
    {
        cout <<  " specialization function "  << endl; 
    }
     void  Func()
    {
        FunImpl<T2>(Type2Type<T2>());
    }
};

 

点评: 通过函数重载实现

 

方法4:

 

template<typename T>
struct  IsString
{
     enum  { value =  false  };
};
template<>
struct  IsString< string >
{
     enum  { value =  true  };
};
template<typename T1, typename T2>
struct  Base
{
     // other function
     // ....
     void  Func()
    { 
         if (IsString<T2>::value)
        {
            cout <<  " specialization function "  << endl; 
        }
         else
        {
            cout <<  " primary function "  << endl; 
        }
    }
};

 

点评: 通过编译时类型判断实现。

 

方法5:

 

template<typename T3,  typename T4>
struct  must_be_same_type
{
     enum  { ret =  0  };
};
template<>
struct  must_be_same_type< string string >
{
     enum  { ret =  1  };
};
template < typename T1,typename T2 >
class  Base{
public :
     // other function
     // ....
     void  Func(){
         if (must_be_same_type<T2,  string >::ret)
        {
            cout <<  " specialization function "  << endl; 
        }
         else
        {
            cout <<  " primary function "  << endl; 
        }
    }
};

 

点评: 和方法4类似, 是不过实现方式不一样。

 

最后,探讨下我自己遇到的问题, 我们在写一个事件委托(delegate)类,大概如下:

 

template<typename return_type, typename first_type, typename second_type>
class  CEvent 
{
public :
     // other function
     // ....
    return_type  operator ()(first_type p1, second_type p2)
    {
        return_type ret = return_type();
         // ...
         // ret = invoker(p1, p2);
         return  ret;
    }
};
void  test3()
{
    CEvent< int int int > e1;
    e1( 1 2 );
    CEvent< void int int > e2;
    e2( 1 2 );
}
int  main()
{
    test3();
}

 

我们可以看到,当return_type是void时, 因为没有返回值,上面的代码会编译失败,因此我们只能偏特化这种情况:

 

template<typename first_type, typename second_type>
class  CEvent< void , first_type, second_type>
{
public :
     // other function
     // ....
     void   operator ()(first_type p1, second_type p2)
    {
         // ...
         // invoker(p1, p2);
         return ;
    }
};

 

但是,我们会发现只有这个operator()函数是需要根据return_type特殊对待的,其他函数永远都是一样的。

我们现在的问题就是如何只特化这个函数。

 

首先我们会想到如下的实现方法:

 

template<typename T>
struct  IsVoid
{
     enum  { value =  false  };
};
template<>
struct  IsVoid< void >
{
     enum  { value =  true  };
};
template<typename return_type, typename first_type, typename second_type>
class  CEvent 
{
public :
    other function
    ....
    return_type  operator ()(first_type p1, second_type p2)
    {
         if (IsVoid<return_type>::value)
        {
            cout <<  " return type is void "  << endl;
             // ...
             // invoker(p1, p2);
        }
         else
        {
            cout <<  " return type is not void "  << endl;
            return_type ret = return_type();
             // ...
             // ret = invoker(p1, p2);
             return  ret;
        }
    }
};

 

但是我们很快会发现这种情况下if语句被编译进去了, 所以return_type是void的情况下还是会编译失败。

我们要解决的问题就是如何把这个if语句变成函数重载,于是我们想到如下实现:

 

template<typename T>
struct  IsVoid
{
     enum  { value =  false  };
};
template<>
struct  IsVoid< void >
{
     enum  { value =  true  };
};
template< int  v>
class  Int2Type
{
     enum  {value = v };
};
template<typename return_type, typename first_type, typename second_type>
class  CEvent 
{
public :
     // other function
     // ....
    return_type InvokerImpl(first_type p1, second_type p2, Int2Type< true >)
    {
        cout <<  " return type is void "  << endl;
         // ...
         // invoker(p1, p2);
    }
    return_type InvokerImpl(first_type p1, second_type p2, Int2Type< false >)
    {
        cout <<  " return type is not void "  << endl;
        return_type ret = return_type();
         // ...
         // ret = invoker(p1, p2);
         return  ret;
    }
    return_type  operator ()(first_type p1, second_type p2)
    {
         return  InvokerImpl(p1, p2, Int2Type<IsVoid<return_type>::value>());
    }
};

 

上面的实现首先通过编译时类型识别,然后再把识别后相应的bool值转成不同类型, 最后再利用不同类型函数重载实现。

 

最后总结下,我们可以看到,从编译时到运行时,从面向对象到普通泛型编程再到模板元编程,C++复杂得让人无语, 也强大得让人无语, 而且C++语言本身是在不断发展的(C++11), 同一问题在C++中往往有多种解决方案,这些解决方案有的简单,有的复杂,有的高效, 也有的低效, 而我们的目标就是利用C++这把利器寻找简单而高效的解决方案。

 

注:本人初学C++ templates编程,如有错误,欢迎指正。

      参考资料:http://bbs.csdn.net/topics/390116038

                    http://bbs.csdn.net/topics/270041821

你可能感兴趣的:(在C++泛型编程中如何只特化类的一个成员函数)