zoukankan      html  css  js  c++  java
  • 在C++泛型编程中如何只特化类的某个成员函数

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

    比如下面这个模板类:

    template<typename T, unsigned B>
    struct Base
    {
        //other function
        
    //....
        void Func(){ cout << "primary function" << endl; }
    };
    void test1()
    {
        Base<int1> a;
        a.Func();
        Base<int16> 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 = voidstruct 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<intint> a;
        a.Func();
        Base<intstring> 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<stringstring>
    {
        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<intintint> e1;
        e1(12);
        CEvent<voidintint> e2;
        e2(12);
    }
    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#Webform 控件
    C#Webform
    MVC Razor 语法
    input file 添加
    ajax
    jquery动画
    jquery选择器,事件 dom操作
    linq 复杂查询
    webform(linq增删改查)
    asp.net内置对象
  • 原文地址:https://www.cnblogs.com/weiym/p/2912563.html
Copyright © 2011-2022 走看看