Linux
Linux software install
Google repo的使用
Vector AP StartApplication编译脚本解析
Yocto的cmake版本升级
不能自动安装的解决方式
S32G-BSP35.0-SDK使用方法
S32G从SDK生成文件系统的制作过程
Linux Samba设置
Linux添加双网卡
S32G USB-Redirector安装指南
VS code自动生成Doxygen格式注释
Linux下使用多线程下载
使用pandoc 生成带中文的pdf
minicom无法输入的问题解决办法
使用 systemd unit 添加一条路由规则
CMake 教程
步骤 1:基本起点
步骤 2:添加lib库
步骤 3:为库添加使用要求
步骤 4:添加生成器表达式
步骤 5:安装和测试
步骤 6:添加支持测试仪表板
步骤 7: 添加系统内省
步骤 8:自定义命令和生成的文
步骤 9:打包安装程序
步骤 10:选择静态库或共享库
步骤 11:添加导出配置
步骤 12:打包 Debug 和 Release
添加虚拟网卡
Vector AP 去掉防篡改校验
Vector AP startapplication编译与使用
Vector AP问题汇总
Vector AP大型项目开发流程
Vector AP EM
Vector AP 最简单的开发示例
Linux kernel 版本降级
Vector AP StartApplicaiton
startappplication-machine-design
startapplicaiton-machine-integration
amsr-vector-fs-em-executionmanager-design
amsr-vector-fs-em-executionmanager
Vector AP 复杂模型的开发
第一章 Machine和MachineDesign
第二章 Execute Manager
第三章 Log
第四章 State Manager
第五章 State Manager 源码的理解
第六章 Someip daemon
第七章 IPC Service Discovery Daemon
crond的使用方法
解决蓝牙鼠标在 Ubuntu 中单位时间内断开的问题
VPS服务器自建教程
v2rayA的客户端使用配置
GDB调试指南入门篇:揭开程序运行世界的神秘面纱
GDB调试指南高级篇:成为调试专家,掌控程序的命运
Linux安装PyQt5并配置Qt Designer
ADB 命令大全
GoogleTest(一)
GoogleTest(二)简单的TEST宏
GoogleTest(三)简单类函数测试
C++ Template
1. 函数模板
2. 类模板
3. 非类型模板参数
软件版本号规范
EPOLL
C++手札
C++ 使用{}初始化有哪些好处?
现代 C++ decltype 深入解析
函数对象(functor)
Linux性能剖析:CPU、内存、网络与I/O压力测试
AP StateManager
C++ Lambda表达式
C++ 中的Lambda表达式
Lambda 表达式语法
Lambda 表达式的示例
手动发送UDP数据包
pyqt5生成的UI界面不能输入中文
自己搭建repo镜像
摄影
Sony仿富士PP值设置
诗词歌赋
本文档使用 MrDoc 发布
-
+
首页
2. 类模板
## 示例 ```cpp #include <vector> #include <cassert> template<typename T> class Stack { private: std::vector<T> elems; public: void push(T const& value); void pop(); T const& top() const; bool empty() const{ return elems.empty(); } }; template<typename T> void Stack<T>::push(T const& value) { elems.push_back(value); } template<typename T> void Stack<T>::pop() { assert(elems.size()); elems.pop_back(); } template<typename T> T const& Stack<T>::top() const { assert(!elems.empty()); return elems.back(); } ``` ## 声明 ```cpp template<typename T> class Stack; ``` ## 定义 ```cpp template<typename T> class Stack { ... }; ``` 这个类的类型是Stack\<T\>,其中T是模板参数。因此,每当在声明中使用此类的类型时,除非模板实参可以推导,否则必须使用Stack\<T\>。 **成员函数的实现** ```cpp template<typename T> void Stack<T>::push(T const& value) { elems.push_back(value); } ``` ## 引用 ```cpp #include <iostream> #include <string> #include "stack1.h" int main(){ Stack<int> intStack; //T是int Stack<std::string> strStack; //T是string intStack.push(7); std::cout << intStack.top() << std::endl; strStack.push("hello"); std::cout << strStack.top() << std::endl; strStack.pop(); } ``` > 只有被调用的模板(成员)函数才会实例化代码。对于类模板,成员函数只有在使用时才会实例化。 > 本例中,默认构造函数、成员函数push()和top()都会为int和string类型实例化出两种类型版本。但成员函数pop()只针对string类型实例化。因为int没有调用pop,如果int也调用了pop,那pop()也会对int类型实例化 ## 类模板的特化 接上面的Stack类,来看一段代码 ```cpp #include <vector> #include <cassert> template<typename T> class Stack { private: std::vector<T> elems; public: .... void printOn(std::ostream & strm) const{ for(T const & e : elems){ strm << e << " "; } } .... }; Stack<std::pair<int, int>> ps; //std::pair<> 并没有定义operator<< 操作符 ps.push({4, 5}); //正确 ps.push({8, 9}); //正确 std::cout << ps.top().first << std::endl; //正确 std::cout << ps.top().second << std::endl; //正确 std::cout << ps << std::endl; //错误 ``` 由于std::pair<>没有operator<< 操作符,导致无法输出。需要对模板做特化。因为我不太熟悉std::pair<>。特化的例子我就用string来示例了。 类模板的特化其实质就是写死`T`的类型。类模板特化时,需要将所有类模板成员函数都做特化。说的带T的成员函数都要做特化。 为了特化类模板,必须用前置template<>的方式声明类,并且声明用来特化类模板的特别定类型。这些类型会用作模板实参,且必须在类名后直接指定。eg: ```CPP template<> class Stack<std::string>{ ... }; ``` 对于这些特化而言,成员函数的任何定义都必须重定义为“普通”成员函数。eg: ```cpp void Stack<std::string>::push(std::string const & elem){ elems.push_back(elem); } ``` 我们看完整示例: stack1.hpp ```cpp #pragma once #include <vector> #include <cassert> #include <iostream> template<typename T> class Stack { private: std::vector<T> elems; public: void push(T const& value); void pop(); T const& top() const; bool empty() const{ std::cout << "I am Stack<T> member function:empty" << std::endl; return elems.empty(); } }; ``` stack2.hpp 你会发现所有成员函数都是重写了 ```cpp #include "stack1.hpp" //把模板类引入进来 #include <deque> #include <string> #include <cassert> #include <iostream> template<> //模板声明不一样了 class Stack<std::string>{ //类声明不一样了 private: std::deque<std::string> elems; //容器不一样了 public: void push(std::string const&); //T变成了std::string void pop(); std::string const & top() const; //T变成了std::string bool empty() const { std::cout << "I am Stack<std::string> member function:empty" << std::endl; return elems.empty(); } }; void Stack<std::string>::push(std::string const& elem) { std::cout << "I am Stack<std::string> member function:push" << std::endl; elems.push_back(elem); } void Stack<std::string>::pop() { std::cout << "I am Stack<std::string> member function:pop" << std::endl; assert(!elems.empty()); //保证不为空 elems.pop_back(); } std::string const & Stack<std::string>::top() const { std::cout << "I am Stack<std::string> member function:top" << std::endl; assert(!elems.empty()); //保证不为空 return elems.back(); } ``` main.cpp ```cpp #include <iostream> #include <string> #include "stack1.hpp" #include "stack2.hpp" //必须把它包含进来,否则还是会去匹配stack1.hpp中的类 int main(){ Stack<int> intStack; Stack<std::string> strStack; intStack.push(7); std::cout << intStack.top() << std::endl; strStack.push("hello"); std::cout << strStack.top() << std::endl; strStack.pop(); } ``` **运行结果:**  从结果得出,int走的通用模板,string走的特化模板。 ### 偏特化 类模板的部分特化处理 ```cpp #include "stack1.hpp" template <typename T> class Stack<T*>{ private: std::vector<T*> elems; public: void push(T*); T* pop(); T* top() const; bool empty() const{ return elems.empty(); } }; template <typename T> void Stack<T*>::push(T* elem){ elems.push_back(elem); } template <typename T> T* Stack<T*>::pop(){ assert(!elems.empty()); T* p = elems.back(); elems.pop_back(); return p; } template <typename T> T* Stack<T*>::top() const{ assert(!elems.empty()); return elems.back(); } ``` 通过 ```cpp template <typename T> class Stack<T*>{ }; ``` 我们定义了一个类模板,仍用`T`来参数化,但是用指针来特化(Stack\<T\*\>) ### 多参数的偏特化 类模板也可以特化多个模板之间的关系,例如,对于一下类模板: ```cpp template<typename T1, typename T2> class MyClass{ .... } ``` 如下偏特化时可以的: 1. 偏特化:两个模板参数具有相同类型 ```cpp //偏特化:两个模板参数具有相同类型 template<typename T> class MyClass<T, T>{ .... } ``` 2. 偏特化:第2个模板参数是int类型 ```cpp //偏特化:第2个模板参数是int类型 template<typename T> class MyClass<T, int>{ .... } ``` 3. 偏特化:两个模板参数都是指针类型 ```cpp //偏特化:两个模板参数都是指针类型 template<typename T1*, typename T2*> class MyClass<T1*, T2*>{ .... } ``` 以下声明,会使用哪个模板: ```cpp MyClass<int, float> mif; //使用MyClass<T1 ,T2> MyClass<float, float> mff; //使用MyClass<T, T> MyClass<float, int> mfi; //使用MyClass<T ,int> MyClass<int*, float*> mif; //使用MyClass<T1* ,T2*> ``` 如果不止一个偏特化同等程度匹配某个声明,则该声明有歧义 ```cpp MyClass<int, int> m; //错误:匹配MyClass<T, T>和MyClass<T, int> MyClass<T*, T*> m; //错误:匹配MyClass<T, T>和MyClass<T1*, T2*> ``` 为了消除第二个歧义,可以提供另一个相同类型的指针的偏特化: ```cpp template<typename T> class MyClass<T*, T*>{ .... } ``` ### 默认类模板参数 ```cpp #pragma once #include <vector> #include <cassert> template <typename T, typename Cont = std::vector<T>> class Stack { private: Cont elems; public: void push(T const& elem); void pop(); T const& top() const; bool empty() const { return elems.empty(); } }; template <typename T, typename Cont> void Stack<T, Cont>::push(T const& elem) { elems.push_back(elem); } template <typename T, typename Cont> void Stack<T, Cont>::pop() { assert(!elems.empty()); elems.pop_back(); } template <typename T, typename Cont> T const& Stack<T, Cont>::top() const { assert(!elems.empty()); return elems.back(); } ``` main.cpp ```cpp #include <iostream> #include <string> #include <deque> #include "stack3.hpp" int main(){ //使用默认类模板参数 Stack<int> intStack; //double 类型栈,使用deque来管理元素 Stack<double, std::deque<double>> dblStack; intStack.push(7); std::cout << intStack.top() << std::endl; intStack.pop(); dblStack.push(53.12); std::cout << dblStack.top() << std::endl; dblStack.pop(); } ```
admin
2024年8月13日 16:06
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码