模板与泛型编程1(函数模板)

定义、实例化函数模板:

对于函数体完全相同,唯一差异就是参数类型的情况,我们可以定义一个通用的函数模板,而非为每个类型都定义一个新函数:

 1 #include <iostream>
 2 #include <vector>
 3 using namespace std;
 4 
 5 template <typename T>//模板参数列表
 6 int compare(const T &v1, const T &v2) {
 7     if(v1 < v2) return -1;
 8     if(v2 < v1) return 1;
 9     return 0;
10 }
11 
12 int main(void) {
13     cout << compare(1, 0) << endl;//T为int
14     cout << compare(vector<int>{1, 2, 3}, vector<int>{2, 3, 4}) << endl;//T为vector<int>
15 
16     return 0;
17 }
View Code

注意:模板定义以关键字 template 开始,后跟一个模板参数列表,里面有一个或多个模板参数在模板定义中,模板参数列表不能为空

类似于函数参数,模板参数表示在类或函数定义中用到的类型或值。当使用模板时,我们 (隐式的或显式地) 指定模板实参,将其绑定道模板参数上。

当我们调用一个函数模板时,编译器(通常)用函数实参来为我们推断模板实参,并用推断出的模板参数来为我们实例化一个特定版本的函数。

 

模板参数可以是模板类型参数或非类型模板参数(表示值而非类型)

 

模板类型参数:

前面的 compare 中 T 就是一个模板类型参数。一般来说我们可以将类型参数看作类型说明符,就像内置类型或类类型说明符一样使用

1 template <typename T>//类型参数
2 //函数模板返回类型与参数类型相同
3 T foo(T *p) {//类型参数可硬用来指定返回类型或函数的参数类型
4     T tmp = *p;//类型参数可以用于函数体内部声明变量或类型转换
5     //...
6     return tmp;
7 }
View Code

注意:类型参数可用来指定返回类型、函数的参数类型、声明变量或类型转换

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

// 错误,U 之前必须加上 class 或 typename

template <typename T, U> T calc(const T&, const U&);

//正确

template <typename T, class U> T calc(const T&, const U&);

注意:在模板参数列表中,class 和 typename 含义完全相同,可以互换使用

 

非类型模板参数:

一个非类型模板参数表示一个值而非一个类型。我们通过一个特定的类型名而非关键字 class 或 typename 来指定非类型参数。当一个模板呗实例化时,非类型参数被一个用户提供的或编译器推断出的值所代替,这些值必须是常量表达式,从而允许编译器在编译时实时实例化模板:

 1 #include <iostream>
 2 #include <string.h>
 3 using namespace std;
 4 
 5 template<unsigned N, unsigned M>
 6 int compare(const char (&p1)[N], const char (&p2)[M]) {//数组不能拷贝,可以引用
 7     return strcmp(p1, p2);
 8 }
 9 
10 int main(void) {
11     compare("hi", "mom");
12 
13     return 0;
14 }
View Code

编译器会实例化出如下模板:

int compare(const char (&p1)[3], const char (&p2)[4])

注意:一个非类型参数可以是一个整型,或者是一个指向对象或函数类型的指针或(左值)引用

绑定到非类型整型参数的实参必须是一个常量表达式。绑定到指针或引用非类型参数的实参必须具有静态的生存期。指针参数也可以用 nullptr 或一个值为 0 的常量表达式来实例化

 

inline 和 constexpr 的函数模板:

template <typename T> inline T min(const T&, const T&);
注意:inline 或 constexpr 说明符放在模板参数列表之后,返回类型之前

 

模板编译:

当编译器遇到一个模板定义时,它并不生成代码。只有当我们实例化(使用)模板的特定版本时,编译器才会生成代码。

通常,当我们调用一个函数时,编译器只需要掌握函数的声明。类似的,当我们使用一个类型的对象时,类定义必须是可用的。但成员函数的定义不必已经出现。因此我们可以将类定义和函数声明放在头文件中,而普通函数和类的成员函数的定义放在源文件中。

模板则不同:为了生成一个实例化版本,编译器需要掌握函数模板或类模板成员函数的定义。因此,与非模板代码不同,模板的头文件通常既包括声明也包括定义。

 

大多数编译错误发生在实例化期间报告:

如,前面的 compare 函数,如果我们用一个没有定义 < 运算符的类型实例化该函数模板,则会发生编译错误

 

定义 find 模板:

 1 #include <iostream>
 2 #include <vector>
 3 #include <list>
 4 using namespace std;
 5 
 6 template<typename T, typename U>
 7 T find(const T &bg, const T &ed, const U &val) {
 8     auto cnt = bg;
 9     while(cnt != ed) {
10         if(*cnt == val) break;
11         ++cnt;
12     }
13     return cnt;
14 }
15 
16 int main(void) {
17     vector<int> vt = {1, 2, 3, 4, 5};
18     list<string> lt = {"jhhg", "fjsl", "zzz", "jfl"};
19 
20     auto cnt = find(vt.begin(), vt.end(), 1);
21     cout << *cnt << endl;
22 
23     auto gg = find(lt.begin(), lt.end(), "zzz");
24     cout << *gg << endl;
25 
26     gg = find(lt.begin(), lt.end(), "aa");
27     if(gg == lt.end()) cout << "//" << endl;
28 
29     return 0;
30 }
View Code

 

编写接受一个数组引用参数的 print 模板:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 template<typename T, unsigned N>
 5 void print(const T (&t)[N]) {
 6     for(int i = 0; i < N; i++) {
 7         cout << t[i] << " ";
 8     }
 9     cout << endl;
10 }
11 
12 int main(void) {
13     int a[10] = {2, 2, 3};
14     print(a);
15 
16     string b[3] = {"fsl", "fj", "z"};
17     print(b);
18 
19     // char *ch = "jfk";//注意,不能传指针,只能传数组
20     print("fjdl");
21 
22     return 0;
23 }
View Code

 

定义接受一个数组实参的 begin、end 模板:

 1 #include <iostream>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 template<typename T, unsigned N>
 6 T* begin_(const T (&a)[N]) {
 7     return const_cast<T*>(a);//底层const只能显式的去除
 8 }
 9 
10 template<typename T, unsigned N>
11 T* end_(const T (&a)[N]) {
12     return const_cast<T*>(a) + N;
13 }
14 
15 template<typename T, unsigned N>
16 void print(const T (&t)[N]) {
17     for(int i = 0; i < N; i++) {
18         cout << t[i] << " ";
19     }
20     cout << endl;
21 }
22 
23 int main(void) {
24     int a[10] = {7, 1, 3, 3};
25     sort(begin_(a), end_(a));
26     print(a);
27 
28     return 0;
29 }
View Code

 

编写一个 constexpr 模板,返回给定数组的大小:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 template<typename T, unsigned N>
 5 constexpr unsigned size(const T (&t)[N]) {
 6     return N;
 7 }
 8 
 9 class gel{
10     int x;
11 };
12 
13 int main(void) {
14     gel g[10];
15     cout << size(g) << endl;
16 
17     return 0;
18 }
View Code

 

posted @ 2018-02-27 23:22  geloutingyu  阅读(457)  评论(0编辑  收藏  举报