模板和泛型编程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模板和泛型编程相关的知识,希望对你有一定的参考价值。

第一次看,看第一节即可

1.我们可以为函数定义一个模板,而不是为每一个类型定义一个函数。

比较函数:

 
  1. #include <iostream>  
  2.   
  3. template <typename T>  
  4. int compare(const T&v1, const T&v2)  
  5. {  
  6.     if(v1 < v2)   
  7.         return -1;  
  8.     if(v1 > v2)  
  9.         return 1;  
  10.     return 0;  
  11. }  
  12.   
  13. int main()  
  14. {  
  15.     int i1 = 10, i2 = 20;  
  16.     double d1 = 20.1, d2 = 20.01;  
  17.     unsigned u1 = 10, u2 = 10;  
  18.     std::cout << compare(i1, i2) << std::endl;  
  19.     std::cout << compare(d1, d2) << std::endl;  
  20.     std::cout << compare(u1, u2) << std::endl;  
  21. }  

 

模板定义以关键字template开始,

后面跟一个参数列表,这是一个逗号分隔的一个或多个模板参数的列表,用< >包围起来。

当我们调用一个函数模板时,编译器会用函数的实参来为我们推断模板实参

 

2.类型参数

类型参数前必须使用关键字class或者typename

在模板参数列表中,这两个关键字的含义相同,可以互换使用,一个模板参数列表中可以同时使用这两个关键字

类型参数在函数中使用我们就把它当作正常类型使用就可以了,比如作为函数的参数,返回值,以及函数内部定义变量等等

 

3.非类型模板参数

除了定义类型参数,我们还可以定义非类型参数,非类型参数表示一个值而非一个类型,我们通过一个特定的类型名而非关键字class或typename来指定。

当一个模板被实例化,非类型参数被一个编译器推断的值所取代。

一个非类型参数可以是一个整形,或者是一个指向对象或函数类型的指针或(左值)引用,绑定到非类型整形参数必须是一个常量,绑定到指针或引用的非类型模板

参数的实参必须有静态生存期。

注意:非类型模板参数的模板必须是常量表达式

inline 和constexpr也可以修饰函数模板,放在返回值前面

 

4.编写类型无关的代码

编写泛型代码的两个重要的原则

<1.模板中的函数参数是const的引用

<2.函数体条件中判断仅仅使用<比较

这两个可以降低对类型的要求

比如const的引用,就保证了函数可以用于不能拷贝的类型。

<比较就可以用于大多数定义了<比较的类型。

结论:模板程序应该尽量减少对实参类型的要求

 

5.模板编译

模板编译和普通的编译不同,它是在使用实例化时编译器才生成代码,为了生成一个实例化版本,编译器需要掌握函数模板或类模板成员函数的定义,

因此和非模板代码不同,模板的头文件通常既包括声明又包括定义。

模板的设计者应该提供一个头文件,包含模板定义以及在类模板或成员定义中用到的所有名字的声明。

模板的用户必须包含模板的头文件,以及用来实例化模板的任何类型的头文件。

 

6.大多数编译错误在实例化期间报告

保证传递给模板的实参支持模板所要求的操作,以及这些操作在模板中能正确工作,是调用者的责任。

 

例题:编写行为类似find算法的模板,函数需要两个模板类型参数,一个表示函数的迭代器参数,另外一个表示值的类型。

 
  1. #include <iostream>  
  2. #include <string>  
  3. #include <list>  
  4. #include <vector>  
  5.   
  6. template<typename iterator, typename T>  
  7. iterator Find(const iterator &beg, const iterator &end, const T&ele)  
  8. {  
  9.     for(auto it = beg; it != end; ++it)  
  10.     {  
  11.         if(*it == ele)  
  12.             return it;  
  13.     }  
  14.     return end;  
  15. }  
  16.   
  17. int main()  
  18. {  
  19.     std::vector<int>ivec1 = {1,2,3,4,5,6,7,8,9,0};  
  20.     auto ret = Find(begin(ivec1), end(ivec1), 3);  
  21.     if(ret == end(ivec1))  
  22.         std::cout << "not find" << std::endl;  
  23.     else  
  24.         std::cout << "find " << *ret << std::endl;  
  25.     std::list<std::string>il = {"111","222","333","444","555"};  
  26.     auto ret2 = Find(il.begin(), il.end(), "333");  
  27.     if(ret2 == il.end())  
  28.         std::cout << "not find" << std::endl;  
  29.     else  
  30.         std::cout << "find " << *ret2 << std::endl;  
  31.       

2.类模板

1.一个类模板的每个实例都形成一个独立的类,但是类和类之间没有关系。

 

<2.我们既可以在类模板外部为其定义成员函数,也可以在类模板内部定义,内部定义被隐式的声明为内敛函数。

<3.类模板的成员函数具有和模板相同的模板参数,因此,定义在模板之外的成员函数就必须以关键字template开始,后接参数列表.

<4.默认情况下,对于一个实例化的类模板,其成员只有在使用时才被实例化。

<5.当我们使用一个类模板类型必须提供模板参数,但有一个例外,在类模板自己的作用域中,我们可以直接使用模板名而不提供实参。

 

模板类和友元

<6.类和友元各自是否是模板是无关的。友元关系被限定在用相同的类型实例化。

<7.一个模板类也可以指定另外一个模板类为友元或者另外一个模板类的特定实例

<9.类模板别名(c++11)

旧标准中,我们只能使用typedef 为特定模板类型定义类型别名。

c++11标准中,我们可以为模板类定义模板类型别名,而且多个参数时可以指定参数是T或者是特定类型。

template <typename T, typename X, typename Z> using TEP = C<T, X, Z>; 

TEP<int, int, int> t;  

<10.类模板的static成员

当类模板包含static成员时(数据或者是函数),每个类模板的实例都有一个static成员,而且定义类模板的static成员时,前面必须加template <参数列表>

3.模板参数

<1.模板参数和作用域

模板参数符合隐藏规则,而且一个模板参数名在参数列表中只能出现一次。

一个特定文件所需要的所有模板的声明通常一起放置在文件开始位置,出现于任何使用这些模板的代码之前。

<2.使用类的类型成员

我们处理模板的时候,必须让编译器知道名字是否表示一个类型

注意:

记住typename和class不是完全相同的,在指明是一个类型而不是名字的情况下必须用typename

比如typedef typename T::size_type = size_type

出处:http://blog.csdn.net/wwh578867817/article/details/43306513

以上是关于模板和泛型编程的主要内容,如果未能解决你的问题,请参考以下文章

模板和泛型编程C++

模板和泛型编程C++

C++ Primer 5th笔记(chap 16 模板和泛型编程)成员模板

C++ Primer 5th笔记(chap 16 模板和泛型编程)可变参数模板

C++ Primer 5th笔记(chap 16 模板和泛型编程)模板类型别名

C++ Primer 5th笔记(chap 16 模板和泛型编程)类模板定义