c++的泛型编程与模板

版权声明:转载需注明出处,若有不足,欢迎指正。 https://blog.csdn.net/qq_28992301/article/details/53575037

c++的泛型编程与模板

“泛型编程”:一种不考虑具体数据类型的编程方式

1.用泛型编程实现类模板

假如现在有个需求,我们要实现一些类,主要用于存储和组织数据结构(如链表类、堆类等),关注其实现的裸机,而不是数据结构中元素的具体类型

  • 使用泛型编程,即实例化类时,向其指定数据类型。这样实现的类十分的精简高效,但其实现本质是根据参数不同定义了不同的类,可以认为类模板是一种语法糖
  • 类模板无法像函数模板那样自动推导类型,只能显示指定类型
template //类模板一般在头文件里定义,template关键字告诉编译器开始泛型编程,
< typename T1,  typename T2, int N>//typename关键字用于声明泛指类型
class Operator
{
public:
    T1 add(T2 a, N)//模板的预处理阶段可以传入参数,只能传递常量,有点类似于宏定义,N就是一个参数
};


template //template关键字告诉编译器开始泛型编程
< typename T1,  typename T2, int N>//如果成员函数在外面定义,则也要加template和typename 

T1  Operator<T1, T2, int N>::add(T2 a, N)
{
    return static_cast<T1>(a + b);
}

int main()
{
    Operator<int> op1;  //实例化类时,必须显示指定类型
    op1.add(1, 2); //调用类中的成员函数
    return 0;
}
  • 类模板是可以特化的,所谓特化,就是对类模板的某一种情况进行定义,当满足该情况时就会优先调用这个特化模板。有点类似于重载,但重载是差异化的,特化是特殊化的
template//原模板
< typename T1, typename T2 >
class Test
{
public:
    void add(T1 a, T2 b)
    {
        cout << "void add(T1 a, T2 b)" << endl;
    }
};

template
< typename T >
class Test < T, T >    // 当 Test 类模板的两个类型参数完全相同时,使用这个实现
{
public:
    void add(T a, T b)//特化的模板可重写原模板的函数
    {
        cout << "void add(T a, T b)" << endl;
    }
    void print()//也可以新增原模板的函数
    {
    }
};

template
<  >
class Test < void*, void* >    //完全特化,当参数都为void*时优先调用
{
public:
    void add(void* a, void* b)
    {
        cout << "void add(void* a, void* b)" << endl;
    }
};

int main()
{  
    Test<int, float> t1;//调用原模板
    Test<long, long> t2;//调用部分特化的模板
    Test<void*, void*> t3;//调用完全特化的模板
    return 0;
}

2.用泛型编程实现函数模板

假设现在有个需求,需要我们实现一个Add接口,该接口可以对两个类型(int、double、string等都有可能)的变量进行相加,并将结果强制类型转换为特定类型再回

  • 那么问题来了,这个函数对于参数的类型很讲究,但是参数类型我们事先是不知道的,那么只能对所有情况进行函数重载。但是这些重载函数仅仅是参数不同,内部逻辑是完全相同的,十分冗余
  • 可以使用“泛型编程”,即类型也作为参数传入。这样实现的Add函数十分的精简高效,但其实现本质仍是函数重载,可以认为函数模板是函数重载的语法糖
template //template关键字告诉编译器开始泛型编程
< typename T1,  typename T2, typename T3>//typename关键字用于声明泛指类型


T1 Add(T2 a, T3 b)
{
    return static_cast<T1>(a + b);
}
  • 此外,函数模板还可以进行“完全特化”,与类模板不同,函数模板不能进行“部分特化”。所谓特化,就是对函数模板的某一种情况进行定义,当满足该情况时就会优先调用这个特化模板。有点类似于重载,但重载是差异化的,特化是特殊化的
template//Add函数模板的一个完全特化
< >
char Add(int a, float b)
{
    cout << "123" << endl;//特化的函数模板可以和原模板不一样
    return static_cast<char>(a + b);
}
  • 函数模板有两种使用方式,自动推导和显式指明,下面以swap为例
    • 若使用自动推导方法,则无法推导出返回值的类型
    • 若使用显式指明,则指明的类型按照之前template < typename T1, typename T2, typename T3>中的顺序一一对应,工程中一般将第一个类型作为返回值类型
int a = 1;
float b = 0;

char c = Add(b, a);//调用原模板,自动推导参数类型,但是无法推导返回值的类型
char d = Add<char, int, float>(a, b);//调用完全特化模板,显式指明类型为T1为char,T2为int,T3为float,
char d = Add<char>(a, b);//当然,也可以偷个懒,只指明返回值类型为char,反正参数类型可以自动推导....

3.普通函数与函数模板的冲突

函数模板也能被重载,不过只根据参数的个数不同来重载罢了,并且工程中,一般只是特化函数模板,并不会去重载函数模板。那么当一个普通的函数及其重载函数,遇到一个同名的函数模板及其特化时,编译器优先调用谁?

  • 当参数匹配时,调用优先级:普通函数及其重载函数>函数特化模板>函数模板
  • 调用时可以使用空的模板实参列表,如Add<>(a, b)来指定编译器调用函数模板

猜你喜欢

转载自blog.csdn.net/qq_28992301/article/details/53575037