cpp_template - ShenYj/ShenYj.github.io GitHub Wiki

模板

简介

泛型,是一种将类型参数化以达到代码复用的技术,C++ 中使用模板来实现泛型

  • 模板的使用格式如下

    • template <typename\class T>
    • typenameclass 是等价的
  • 模板没有被使用时,是不会被实例化出来的

  • 模板的声明和实现如果分离到.h.cpp中,会导致链接错误

  • 一般将模板的声明和实现统一放到一个.hpp文件中

  • 示例代码

    template <typename T> T add(T a, T b) {
        return a + b;
    }
    
    class Point {
    public:
        int m_x;
        int m_y;
    
        Point(int x, int y) :m_x(x), m_y(y) { }
        
        Point operator+(const Point &p) const {
            return Point(this.m_x + p.m_x, this.m_y + p.m_y);
        }
    };
    
    int main() {
    
        /// 按照常规重载的写法,也不会报错, 编译器也会自动推导出类型
        cout << add(10, 20) << endl;
        cout << add(1.5, 21.5) << endl;
        cout << add(Point(10, 20), Point(11, 25)) << endl;
    
        /// 模板的标准写法
        add<Int>(10, 20);
        add<Float>(1.5, 21.5);
        add<Point>(Point(10, 20), Point(11, 25));
        
        return 0;
    }

编译细节

编译器会根据参数的不同,调用不同的函数,不同的函数由编译器自动生成

  • 示例代码

    template <typename T> 
    T add(T a, T b) {
        return a + b;
    }
    
    class Point {
    public:
        int m_x;
        int m_y;
    
        Point(int x, int y) :m_x(x), m_y(y) { }
        
        Point operator+(const Point &p) const {
            return Point(this.m_x + p.m_x, this.m_y + p.m_y);
        }
    };
    
    int main() {
    
        /// 模板的标准写法
        add<Int>(10, 20);                           /// 汇编: call add<int>(函数地址1)
        add<Float>(1.5, 21.5);                      /// 汇编: call add<Float>(函数地址2)
        add<Point>(Point(10, 20), Point(11, 25));   /// 汇编: call add<Point>(函数地址3)
        
        return 0;
    }

如果没有使用一个模板函数,编译器不会自动生成函数

  • 模板的声明和实现如果分离到.h.cpp中,会导致链接错误, 错误的演示代码:

    • demo.h 文件

      #pragma once
      /// 声明
      template <typename T> T add(T a, T b);
    • demo.cpp 文件

      #include "demo.h"
      
      /// 实现
      template <typename T> 
      T add(T a, T b) {
          return a + b;
      }
    • main.cpp

      #include "demo.h"
      
      int main() {
      
          /// 编译能通过
          add(1, 2);
          add(1.5, 3.5);
          return 0;
      }

理解模板不支持函数声明和实现分离前,先要了解以下关键点

  • include 预处理指令在预处理阶段被删除,将包含的文件插入到该预编译指令的位置

  • 一开始编译后 call 函数地址并不是准确的地址,在链接的时候,会修正函数地址

  • 模板没有被使用时,是不会被实例化出来的,声明实现分离后编译期不存在函数调用,就不会自动生成函数实现

所有的函数调用最终会被转换成汇编代码:call 函数地址, 编译阶段虽然有函数的声明,但是 demo.hpp 编译成目标文件后,并不包含函数实现,在链接阶段要修正函数调用的真正地址,由于找不到真正实现,导致链接失败

所以使用模板时,不要将声明和实现分离在不同的文件中,一般将模板的声明和实现统一放到一个.hpp 文件中
外界使用时直接 #include "add.hpp", 比如在 main.cpp中使用, 这样声明实现就都会和main.o目标文件在一起,编译期自动生成对应函数实现的作用

  • add.hpp

    #pragma once
    
    template <typename T> 
    T add(T a, T b) {
        return a + b;
    }

类模板

  • 示例:动态数组

    .

类模板中的友元

  • 友元函数中包含泛型,c++语法上需要特殊处理, 函数名后需要加上<>泛型符号(声明和实现都需要)

    .

⚠️ **GitHub.com Fallback** ⚠️