• ### 学习《C++ Primer》- 9


    Part 9: 模板与泛型编程(第16章)

    // @author:       gr
    // @date:         2016-03-18
    // @email:        forgerui@gmail.com
    

    1. 模板参数

    1. 类型模板参数
      类型可以是类类型或内置类型,通过typename或class修饰类型。

       //T是一个类型
       template <typename T>
       void func(const T & rhs) {
       	//....
       }
      
    2. 非类型模板参数
      非类型参数表示一个值,不需要typename,直接使用特定类型名修饰:

       template <unsigned N, unsigned M>
       void func(const char (&p1)[N], const char (&p2)[M]) {
       	return strcmp(p1, p2);
       	//...
       }
      

      我们可以如下调用

       compare("hi", "kitty");
      

      编译器会使用字面常量的大小来代替N和M。因此编译器会实例化出如下版本:

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

    2. 模板编译

    为了生成一个具现化模板,编译器需要掌握函数模板或类模板的定义。

    因此,模板的头文件通常既包括声明也包括定义

    编译器会在三个阶段报错:

    第一个阶段是编译器模板本身。
    第二个阶段是编译器遇到模板使用时。
    第三个阶段是模板实例化时,只有这个阶段才能发现类型相关的错误。

    3. 类模板

    接受一个initializer_list参数:

    template <typename T>
    Blob<T>::Blob(std::initializer_list<T> il) : data(std::make_shared<std::vector<T>>(il)) {}
    

    一个实例化的类模板,其成员只有在使用时才进行实例化。

    4. 类模板名的简化

    在类内,可以简化类模板名,去掉模板类型名。
    在类外,必须使用完整的类模板名

    5. 模板友元

    一个类可以将另一个模板的每个实例都声明为友元,或者限定特定的实例为友元:

    template <typename T> 
    class C {
    	//C的每个实例将相同实例化Pal声明为友元
    	firend class Pal<T>;
    	//Pal2的所有实例都是C的每个实例的友元
    	template <typename X> friend class Pal2;
    	//Pal3是一个非模板类,它是C所有实例的友元
    	friend class Pal3;
    };
    

    6. 模板类型别名

    可以使用typedef定义指定的类型参数模板:

    typedef Blob<string> StrBlob;
    

    但不能用typedef引用一个模板,即typedef无法引用Blob

    但新标准允许定义为类模板定义一个类型别名:

    template <typename T> using twin = pair<T, T>;
    twin<string> authors;   //相当于pair<string, string> authors;
    

    7. 类模板的static成员

    相同模板参数实例化的类之间才共享静态变量和静态函数。

    8. 默认模板实参

    可以提供默认模板实参,新标准可以为函数和类模板提供默认实参。以前只允许为类模板提供默认实参。

    template <typename T, typename F = less<T>>
    int compare(const T &v1, const T &v2, F f = F()) {
    	if (f(v1, v2))  return -1;
    	if (f(v2, v1))  return 1;
    	reutrn 0;
    }
    

    调用时,可以提供自己的比较操作,但并不必需:

    bool i = compare(0, 42);
    
    Sales_data item1(cin), item2(cin);
    bool j = compare(item1, item2, compareIsbn);
    

    9. 模板默认实参与类模板

    如果类模板为其所有模板参数都提供了默认实参,如果要使用这些默认实参,就必须在模板名之后跟一个空尖括号对:

    template<class T = int>
    class Numbers {
    public:
    	Numbers(T v = 0) : val(v) {}
    private:
    	T val;
    };
    
    Numbers<long double> lots_of_precision;
    Numbers<> average_precision;     // 空<>表示我们希望使用默认参数
    

    10. 类模板的成员模板

    类模板中的成员函数也可以定义成模板,如果这个成员模板在模板类外定义,那要写两个模板参数列表:

    template <typename T>   //类的类型参数
    template <typename It>  //函数的类型参数
    Blob<T>::Blob(It b, It e) {
    	//...
    }
    

    11. 模板类型转换

    模板只接受两种类型转换:

    1. 非const转const:将非const引用对象转换为const引用对象
    2. 指针转换:数组实参可以转换为指向其首元素的指针,函数实参可以转换为该函数类型的指针

    其它的类型都不能被自动转换。

    12. 函数模板显式实参

    函数模板如果位于参数中,可以直接通过参数列表推导出来。
    函数模板如果参数类型是返回值,这样就无法自动推导出来,这样就需要在函数后显式指定。

    template <typename T1, typename T2, typename T3>
    T1 func(T2, T3);
    
    auto val1 = func<string>(1, 1.0);    //可以,相当于string func(int, double)
    auto val2 = func<string, int, double>(1, 1.0);  //可以,全部显式指定
    

    省略的参数类型必须放在显式指定的后面,否则不能省略,全部类型都要显式给出:

    template <typename T1, typename T2, typename T3>
    T3 func(T1, T2);
    auto val1 = func<string>(1, 1.0);    //错误
    auto val2 = func<int, double, string>(1, 1.0);    //可以,必须全部给出类型
    

    13. 正常类型转换应用于显式指定的实参

    上面11条说模板不支持大多数的转换,但如果模板实参已经被显式指定,那么这种转换就可以进行。

    template <typename T>
    void func(T, T);
    
    long lng;
    func(lng, 1024);    //无法进行,两个参数不一致,且支持类型转换
    func<int>(lng, 1024);  //可以进行,long被转换成int
    func<double>(lng, 1024);  //可以进行,两者都被转换为double
  • 相关阅读:
    解决iex -S mix报错
    OTP服务器
    多进程
    字符串与二进制
    IphoneX适配正确姿势
    Babel 配置用法解析
    babel版本变化
    你好,babel
    XSS攻击 && CSRF攻击 基础理解
    小程序开发初体验~
  • 原文地址:https://www.cnblogs.com/gr-nick/p/5742060.html
Copyright © 2020-2023  润新知