cpp_template - ShenYj/ShenYj.github.io GitHub Wiki
泛型,是一种将类型参数化以达到代码复用的技术,C++ 中使用模板来实现泛型
-
模板的使用格式如下
template <typename\class T>
-
typename
和class
是等价的
-
模板没有被使用时,是不会被实例化出来的
-
模板的声明和实现如果分离到
.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++语法上需要特殊处理, 函数名后需要加上
<>
泛型符号(声明和实现都需要)