zoukankan      html  css  js  c++  java
  • 模板与泛型编程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
  • 相关阅读:
    [转载] CSS模块化【封装继承多态】
    【转】jquery图片播放插件Fancybox使用方法
    指定打印宽度,左&右对其
    预测编码与帧间压缩方法
    字符串
    静态变量 static
    利用getchar, putchar复制文件
    排序
    printf 语句
    Ubuntu 宽带连接
  • 原文地址:https://www.cnblogs.com/geloutingyu/p/8476763.html
Copyright © 2011-2022 走看看