zoukankan      html  css  js  c++  java
  • C++模板——读书总结篇

    函数模板

    定义

    • 例子一

      template<typename T>
      void Func() {
      	cout << "hello, world" << endl;
      }
      
    • 例子二

      template <typename T, template<typename,typename> class A, size_t N>
      void Func(T input) {
      	T a = input;
      	A<int , double> a;
      	int array[N] = {};
      }
      

    实例化

    • 隐式实例化

      1. 编译器使用函数实参来推断模板实参进行实例化。
      2. 对于返回值类型为模板参数的情况,编译器无法推断,必须显示指定。
      3. 当我们使用一个函数模板初始化一个函数指针时,编译器会使用函数指针的类型来推断模板实参的。如果不能唯一实例化(编译器推断不出来了),该操作就会失败。
    • 显示实例化
      当隐式无法实例化时,可以显示实例化。 例如:

      // 函数模板的声明与定义
      template<typename RetType, typename T>
      RetType Func(T input) {
      	RetType a = 1;
      	return a;
      }
      
      // 函数模板的实例化
      double a = 100.4;
      Func<int>(a);
      

    类模板

    模板的定义

    1. 举例一: 普通的定义

      template<typename T>
      class Base {
      };
      
    2. 举例二:使用模板类作为类型参数

      template<typename T>
      class Base {
      };
      
      // 模板类作为类型形参数。
      template<typename T, template<typename M> class Base>
      class Other {
      	
      }
      
    3. 模板类的成员函数:对于类模板的成员函数,即可以在类内定义(默认为inline),也可以在类外定义(需要使用template关键字开始)。

      // 类内定义
      template<typename T>
      class A {
      public:
      	void Print() {
      		cout << "hello, world" << endl;
      	}
      };
      
      // 类外定义
      template<typename T>
      class A {
      public:
          void Print();
      }
      
      template<typename T>
      void A<T>::Print() {
          cout << "hello, owrld" << endl;
      }
      
    4. 类模板的类型名的简写: 在类模板自己的作用域时,可以直接使用类模板名,而不需要类模板名<类型参数> , 就是相当于简化了。例如:

      template<typename T>
      class A {
      public:
      A<T>& GetInstance() {
      	A* instance = new A;  // A<T> 可以简写为A.
      	return *instance;
      }
      };
      

    类模板的实例化

    1. 默认情况下,一个类模板的成员函数只有在使用时,它才被实例化。所以呢, 有一些成员函数无法实例化时, 只要不使用它,就没有问题。
    2. 使用类模板时, 必须使用在<>中加上类型参数 进行显式实例化。

    类模板与友元

    1. 当一个类包含一个友元时,类与友元各自是否为模板是相互无关的。如果一个类包含了一个非模板的友元,则友元被授权可以访问所有模板实例。 如果友元自己是模板,类可以授权给所有友元模板实例。
    2. 为了引用一个模板的特定实例,必须先声明模板自身。
    3. 在新标准中,可以将模板类型参数声明为友元。
      template<typename type> 
      class Bar { friend Type;};
      

    模板类的别名

    1. typedef 不允许为模板定义一个类型别名,因为模板不是一种类型。

    2. 使用using 可以为模板定义一个别名,例如:

      template<typename T, typename M> 
      using twin = pair<T, M>;
      
      template<typename T>
      using ttt = pair<T, int>;
      
      twin<int, char> a;
      ttt<double> b;
      

    模板类内的类型成员

    1. 在模板内部可以定义一种类型,但是在使用的时候存在问题,编译器无法区别使用的类型还是类内的static 变量?例如 : T::new_type, 这个new_type到底是变量还是类型呢?
    2. 默认情况下, c++语言假定通过作用域运算符访问的名字不是类型
    3. 当我们使用类型时, 使用typename 来标识它,例如: typename T::new_type a(定义变量a)。

    成员函数模板

    1. 一个类(无论是否为模板类),都只可以是模板的成员函数,这种成员叫作成员模板。

    2. 成员模板不可以是虚函数。

    3. 普通类的成员模板,即普通类的成员函数为模板函数,性质与函数模板相同。

    4. 类模板的成员模板:
      a. 类与成员模板,它们有各自的独立的模板参数。
      b. 当我们在类模板之外定义一个成员模板时,必须同时为类模板与成员模板提供参数列表。
      c. 类模板的参数列表在前,成员模板的模板参数在后。

      // 类模板声明
      template<typename T>
      class A {
      public:
      	template<typename M>
      	void Print();
      };
      
      // 成员函数的定义
      template<typename T>
      template<typename M>
      void A<T>::Print() {
      	cout << "hello" << endl;
      }
      
      // 使用
      A<int> a;
      a.Print<double>();
      

    模板的参数

    1. 对于模板参数,即可以为类型,也可以为非类型参数,也可以为模板类。

    2. 当模板的参数为非类型参数时,
      a. 它为一个值,而非类型,编译期可以确认的值。
      b. 它只可以为整型、可以为对象或函数类型的指针或左值引用。(对象必须有静态生命周期,就是全局变量了。 如果是static 变量,需要是const的,原因是非const的static变量的地址编译器可能无法确定,因为的是文件作用域,我猜的)

      // 类型参数
      template<typename T>
      void Print();
      
      // 非类型参数
      template<size_t M>
      void Print();
      
      // 模板类参数
      template <template<typename> class A>
      void Print();
      
      // 指针或引用
      template<int* p>
      void Print() {
          cout << 指针 << endl;
      }
      
      template<int& v>
      void Print() {
      cout << 引用 << endl;
      }
      
      int a = 10;
      int& b = a;
      Print<&a>();
      Print<b>();
      
    3. 默认参数
      a. 例如:template<typename T = int, typename Y = double>
      b. 对于模板参数,只有当它右侧的所有参数都有默认实参时,它才可以有默认实参。

    模板的实参的推断

    1. 指的是函数模板中根据实参类型进行推断形参。

    2. 对于函数模板来说,只有两种类型转换(隐式转换).除此之外,算术类型的转换、派生类到基类的转换、用户自定义的转换等都是不允许的
      a). const 转换,一个非const 对象的引用或指针的实参可以传递给一个const的引用或指针的形参。
      b). 数组与函数指针转换:如果函数不是引用类型(当是引用类型时,数组的大小不一样,类型不同)时,可以对数组或函数类型的实参应用正常的指针转换。即数组转换为指针,函数转换为函数指针。

    3. 在函数模板中,如果使用到普通类型定义的参数, 它们是可以正常转换的。 只有 模板的类型参数不可以正常转换。

    4. 正常的类型转换也可以应用于显示指定的实参的。

    5. 当我们使用一个函数模板初始化一个函数指针时,编译器会使用函数指针的类型来推断模板实参的。如果不能唯一实例化(编译器推断不出来了),该操作就会失败。

    6. 重量级知识点:引用折叠与右值引用参数:
      a): 当模板参数为左值引用时,只能传递给它的一个左值的实参。
      b): 当模板参数为const 左值引用时,传递给它的一个左值的实参/临时对象、字面值等
      c): 神奇到来:当模板函数参数是一个右值引用时,传递给它的实参可以是任意类型(也就是说左值也可以),原因就是因为引用折叠。
      d): 例外规则一: 当我们将一个左值传给模板函数的右值引用参数(T&&)时, 编译器推断模板类型参数为的左值引用类型,例如为int类型时,推断T为int&.
      e): 例外规则二:如果我们间接创建了一个引用的引用,则这些引用形成了引用折叠。正常情况下,不能直接创建引用的引用,但是可以间接创建。大部分情况下,引用的引用会折叠为普通的左值引用(T& &、T& &&、 T&& &),右值引用的右值引用,分折叠成右值引用。
      f): 通过两个规则,得出一个结论:如果一个函数模板参数为右值引用,则可以传递给它任意类型的实参

    7. 引用折叠,引出的std::move的实现:

      template <typename T>
      typename remove_reference<T>::type&& move(T&& t) {
      return static_cast<typename remove_reference<T>::type&&>(t);
      }
      

    控制模板的实例化

    1. 编译器对模板的处理:当编译器遇到模板定义时,并不生成代码,而是当实例化模板时,才会生成代码。也就是说,使用的时候才生成代码。

    2. 通常情况下,当模板被使用时,才会进行实例化。 这意味着,相同的实例可能出现在多个对象文件中。在大的系统中,多个文件实例化相同模板的开销会很大,后处理时还要相办法只保留一份。

    3. 在新的标准中,可以通过显示实例化来控制实例化的过程。

    4. 实例化的声明,格式为: extern template declaration, 其中declaration 是类或函数声明,并且其中模板参数都已经被替换为模板的实参。在实例化的声明与定义之前,都应该已经定义好了模板类或者模板函数,否则编译报错

      // 实例化的声明
      extern template class Blob<int>;
      extern template Func(int a);
      
      // 模板类或模板函数的声明,注意区分它们。
      template< typname T>
      class A;
      template<typename T>
      void Func();
      
    5. 实例化定义, 格式为:template declaration

      // 实例化的定义
      template class Blob<int>;
      template Func(int a);
      
    6. 额外知识点:
      a). 当编译器遇到extern时,不会在本文件中生成实例化的代码。
      b). 将一个实例化的声明为extern就表示了在程序的其它位置有该实例化(不包含extern)
      c). 一个类模板的实例化定义会实例化所有的成员函数。 这与处理类模板的普通实例化是不相同的(通常情况下,使用到的时候才进行实例化)

    模板的特例化

    1. 特例化, 就是我们人工地为编译器针对模板做了推导工作,遇到相同参数类型时,请使用人工给的模板样例,编译器别自己推断了。

    2. 特例化与实例化是不同的。 实例化,编译器会生成实例代码的,比如一个函数模板,实例化时,会生成对应的函数。特例化只是给了一个人工的模板,编译器实例化时,请优先使用我给的特例化模板。

    3. 全特例化:把所有的形参都进行特例化.

      template <typename T1, typename T2>
      void Func() {
      	cout << "hello" << endl;
      }
      
      template<>
      void Func<int ,double>() {
      	cout << "wow" << endl;
      }
      
    4. 部分特例化:
      a). 类模板可以部分特例化,函数模板不可以部分特例化。
      b). 部分特例化分两种:特例化一部分参数和特例化参数的一部分。

      // 特例化一部分参数
      template <typename T1, typename T2>
      class A {
      public:
          A() { cout << "general" << endl; }
      };
      
      
      template<typename T>
      class A<int, T> {
      public:
          A() { cout << "first int" << endl; }
      };
      
      template<typename T>
      class A<T, double> {
      public:
          A() { cout << "first double" << endl; }
      };
      
      A<char, char> a;
      A<int ,char> b;
      A<char, double> c;
      
      // 特列化参数的一部分
          template<typename T>
      class A { 
      public:
          A() { cout << "general " << endl;}
      };  
          
      template<typename T>
      class A<T&> {
      public:
          A() { cout << "reference " << endl;}
      };  
          
      template<typename T>
      class A<T*> {
      public:
          A() { cout << "pointer " << endl;}
      };  
          
      A<int> a;
      A<int&> b;
      A<int*> c;
      
      
      
      
  • 相关阅读:
    Mysq数据库备份(win)
    Mysql保存中文乱码问题
    MySql常用操作
    win下 mysql远程连接设置
    windows下redis的使用
    栈和队列
    ffmpeg 常用命令
    nginx https配置模板
    openssl 、nginx生成配置自签名证书
    https、公钥,私钥,数字证书
  • 原文地址:https://www.cnblogs.com/yinheyi/p/14729777.html
Copyright © 2011-2022 走看看