在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实现,跟方法7也有点类似。

我们可以看到根据编译时模板参数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


原出处:http://www.cnblogs.com/weiym/archive/2013/02/14/2912563.html

部分代码或文字经本人整理或修饰) 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: "多范型设计"是一种软件架构设计方法,它旨在在一个系统实现不同的变换及操作,并试图最大化代码的重用性。这种方法不仅允许在设计过程创建灵活的系统结构,还可以降低开发成本和复杂度。范型设计的核心思想是将系统分解为相互独立的部分,然后将每个部分都定义为可重用的代码块,这些代码块能够为不同的场景和应用程序提供共同的服务和功能。在设计多范型系统时,开发人员通常会使用一些基于、继承和多态等重要的构造块,这些构造块可以定义系统的核心元素和行为。范型设计的优点在于它可以提高系统的灵活性、可维护性和可扩展性,同时减少了系统内部的耦合,增强了代码重用性。范型设计也有它的缺点,在实现过程,可能会导致过度的抽象和模板化,增加了系统的复杂性,并且需要一定程度的专业知识和实践经验。综合来看,多范型设计可以让人们更好地理解系统结构和代码复用。 ### 回答2: 多范型设计(Multi-Paradigmatic Design)是指通过结合多种不同范型(Paradigms)的最佳实践,来创新性地解决设计问题的方法和理念。范型是一种思维模式和方法论,它主要指导着人们如何看待世界、如何分析问题。在设计领域,不同的范型有着不同的侧重点和解决问题的方式。 在多范型设计,设计师会探索不同范型之间的相互联系和交叉点,以实现最佳的解决方案。例如,他们可能会从功能性、美学性、可用性等多个方面来考虑设计问题,以达到多方面的满足。同时,设计师也会借鉴不同范型之间的优势和创新性思维,在其创造出属于自己的设计范式。 通过多范型设计,设计师们能够跨越不同领域的边界,将来自不同范型的创意成果结合起来,从而创造出更切实可行且具有创新性的设计方案。因此,多范型设计被广泛应用于各个设计领域,并带来了更加综合和富有创意的设计成果。 ### 回答3: 多范型设计是一种设计模式,其目的是通过使用不同的范型来实现可重用和灵活的代码。C++是一种支持多范型设计的语言,因为它可以支持面向过程的编程、面向对象的编程泛型编程。多范型设计可以增强代码的可读性和可维护性,并且可以提高软件开发的效率。 使用多范型设计可以避免重复编写相似的函数和,同时可以使代码更加灵活和可扩展。多范型设计可以使函数和更具有通用性,并且可以避免因为特定数据型的限制而导致的代码冗余。例如,使用模板可以避免重复编写一个函数或的多个版本。 需要注意的是,使用多范型设计需要平衡泛化和特化。过于泛化会导致代码过于抽象和复杂,而过于特化则会导致代码的可读性和可重用性降低。因此,设计者需要根据实际需求来权衡范型的使用。 总之,多范型设计是一种强大的编程技术,它可以使代码更加可重用和灵活,并且可以提高软件开发效率。设计者需要根据实际需求灵活运用多种范型,以实现代码的最佳设计。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值