C++模板 怼烎@ 2023-05-31 06:56 16阅读 0赞 # 1. 为什么要使用模板? # 1. 假如设计一个求两参数最大值的函数,在实践中我们可能需要定义四个函数: ![format_png][] 2. 这些函数几乎相同,唯一的区别就是形参类型不同。 3. 需要事先知道有哪些类型会使用这些函数,对于未知类型这些函数不起作用。 4. 其他可替代方法对比 <table> <thead> <tr> <th>替代方法</th> <th>缺点</th> </tr> </thead> <tbody> <tr> <td>重载方式</td> <td>相同的代码复制了多次,有修改时候,多处相同代码都需要修改</td> </tr> <tr> <td>借助父类,子类继承父类</td> <td>1. 缺少类型检测的功能;2. 以后子类都需要继承父类,代码难以维护</td> </tr> <tr> <td>预处理命令</td> <td>1. 格式混乱; 2. 功能需求比较大时候,无法满足需求</td> </tr> </tbody> </table> # 2.模板的概念 # 1. 所谓模板是一种使用**无类型参数**来产生一系列**函数**或**类**的机制。 2. 若一个程序的功能是对某种特定的数据类型进行处理,则可以将所处理的**数据类型说明为参数**,以便在其他数据类型的情况下使用,这就是模板的由来。 3. 模板是以一种**完全通用的方法**来设计函数或类而不必预先说明将被使用的每个对象的类型。 4. 通过模板可以产生类或函数的集合,使它们操作不同的数据类型,从而**避免需要为每一种数据类型产生一个单独的类或函数**。 # 3 模板的分类 # 1. 模板分为函数模板(模子)和类模板(模子),允许用户分别用它们构造(套印)出(模板)函数和(模板)类。 2. 图显示了模板(函数模板和类模板),模板函数,模板类和对象之间的关系。 ![format_png 1][] ## 3.1 函数模板 ## ### 定义格式 ### template <模板形参表> <返回值类型> <函数名>(模板函数形参表) { //函数定义体 } 其中T为类型参数,它可用基本类型或用户自定义的类型。 模板形参表格式如下: class <参数名> 或typename<参数名> 或<类型修饰> <参数名> class 与typename 是没有却别的,class 出现的比较早,现在一般使用typename template <typename T> T const& max(T const & a, T const & b) { return a < b ? b : a; } ### 使用模板 ### 模板函数的使用,只需要传入对应的值即可 std::cout << "max(7, 41):"<< ::max(7, 42) <<std::endl; // 显示指定类型 std::cout << "max(7, 42):"<< ::max<int>(7, 42) <<std::endl; std::cout << "max(7.0, 42.0):"<< ::max(7.0,42.0) <<std::endl; std::cout << "max<int>(7.0, 42.0):"<< ::max<int>(7.0,42.0) <<std::endl; std::cout << "max<double>(7.0, 42.0):"<< ::max<double>(7.0,42.0) <<std::endl std::string s1 = "aaaa"; std::string s2 = "aaaabb"; std::cout << "max(s1,s s2):"<< ::max(s1, s2) <<std::endl; 两个参数类型不相同时候会报错,例如 max(7.0, 42) ### 解决方法: ### 1.每个模板参数独立类型 template <typename T , typename U> inline const T& Max(const T& a, const U& b){ return a>b?a:b; } > 注意:这种解决方法还有一个问题,就是返回值只能强制设置为T或者U,不能自动推导。 > C++11的后置推导可以解决这个问题。 template <typename T, typename U> inline auto Max(const T& a, const U& b)->decltype(a>b?a:b) { return a>b?a:b; } 2.显示指定模板实参类型 Max<int>(2,2.4) 或者 Max<double>(2,2.4) 3.实参强制类型转换 Max(2,static_cast<int>(2.4)) 或者 Max(static_cast<double>(2),2.4) -------------------- ### ### candidate template ignored: deduced conflicting types for parameter 'T' ('double' vs. 'int') inline T const& max(T const & a, T const & b) 注:通常而言,并不是把模板编译成一个可以处理任何类型的单一实体;而是对于实例化模板参数的每种类型,都从模板产生一个不同的实体。这种用具体类型代替模板参数的过程叫做**实例化**,它产生了一个模板的实例。 于是,模板被编译了两次,分别发生在: (1)实例化之前,先检查模板代码本身,查看语法是否正确;在这里会发现错误的语法,如遗漏分号等。 (2)在实例化期间,检查模板代码,查看是否所有的调用都有效。在这里会发现无效的调用,如该实例化类型不支持某些函数调用(该类型没有提供模板所需要使用到的操作)等。 ### 重载函数模板 ### template <typename T> inline T const& max(T const & a, T const & b) { std::cout << "all" << std::endl; return a < b ? b : a; } inline int const& max(int const & a, int const & b) { std::cout << "int" << std::endl; return a < b ? b : a; } int main() { int a = 7; int b = 42; int maxInt = ::max(a,b); std::cout << maxInt << std::endl; // 调用非模板 std::cout << max<>(a,b) << std::endl; // 调用模板 std::cout << max<int>(a,b) << std::endl; // 调用模板 std::cout << max(1.0,2.0) << std::endl; // 调用非模板 std::cout << max("aaa", "bbb") << std::endl; // 调用模板 std::cout << max('42', 1) << std::endl; // 调用非模板 } 1. 对于非模板函数和同名的函数模板,如果其他条件都是相同的话,那么在调用的时候,重载解析过程通常会优**先调用非模板函数**,而不会从该模板产生出一个实例。然而,如果模板可以产生一个具有更好匹配的函数,那么将选择模板。 2. 可以**显式地指定一个空的模板实参列表**,这个语法好像是告诉编译器:只有模板才能匹配这个调用(即便非模板函数更符合匹配条件也不会被调用到),而且所有的模板参数都应该根据调用实参演绎出来。 3. 因为**模板是不允许自动类型转化**的;但**普通函数可以进行自动类型转换**,所以当一个匹配既没有非模板函数,也没有函数模板可以匹配到的时候,会尝试通过自动类型转换调用到非模板函数(前提是可以转换为非模板函数的参数类型)。 ## 3.2. 类模板 ## ### 定义格式 ### template<typename T> class 类名 { //… }; 关键字typename(或class)后面的T是类型参数。在实例化类定义中,欲采用通用数据类型的数据成员,成员函数的参数或返回值,前面需要加上T。 类模板的内部可以想其他类一样,声明成员变量和成员函数; 在成员函数的实现中必须要限定这个模板类,成员函数实现逻辑就是函数模板; ### 栈的类模板 ### #include <vector> #include <stdexcept> template <typename T> class Stack { private: std::vector<T> elems; public: void push(T const & e); // 入栈 void pop(); // 出栈 T top() const; // 返回栈顶元素 bool empty() const { // 是否空 return elems.empty(); } }; 成员函数实现 // 入栈 template <typename T> void Stack<T>::push(T const& e) { elems.push_back(e); } // 出栈 template <typename T> void Stack<T>::pop() { if (elems.empty()) { throw std::out_of_range("out of range"); } T e = elems.back(); elems.pop_back(); // 删除最后一个元素 return e; } // 返回栈顶原始 template <typename T> T Stack<T>::top() const { if (elems.empty()) { throw std::out_of_range("out of range"); } return elems.back(); // 返回最后一个元素的拷贝 } 类模板不代表一个具体的、实际的类,而代表一类类。实际上,类模板的使用就是将类模板实例化成一个具体的类,它的格式为: 类名<实际的类型>对象名; 例如,使用上面的类模板,创建模板参数为char、int型的对象,语句如下: Stack<char> charStack ; Stack<int> intStack; intStack.push(1); intStack.push(2); std::cout << intStack.top() << std::endl; 1. 只有那些被调用的成员函数,才会产生这些函数的实例化代码。对于类模板,成员函数只有在被使用的时候才会被实例化。显然,这样可以节省空间和时间; 2. 另一个好处是,对于那些“未能提供所有成员函数中所有操作的”类型,你也可以使用该类型来实例化类模板,只要对那些“未能提供某些操作的”成员函数,模板内部不使用就可以。 3. 如果类模板中含有静态成员,那么用来实例化的每种类型,都会实例化这些静态成员。 切记,要作为模板参数类型,唯一的要求就是:该类型必须提供被调用的所有操作 ### 类模板的特化 ### 特化 和 重载类似,重载是函数名相同,形参不同,特化就是类名相同,类的具体类型不同; 为了特化一个类模板,你必须在起始处声明一个template<>,接下来声明用来特化类模板的类型。这个类型被用作模板实参,且必须在类名的后面直接指定: template<> class Stack<std::string> { ... }; 进行类模板的特化时,每个成员函数都必须重新定义为普通函数,原来模板函数中的每个T也相应地被进行特化的类型取代。如: void Stack<std::string>::push(std::string const& elem) { elems.push_back(elem); } 局部特化 上面的特化,类模板被具体类型代替、所有的成员函数被重新定义,这个叫做全特化;有时候要求模板参数仍由用户控制,这个叫做偏特化或者局部特化; 类模板 template <typename T1, typename T2> class Myclass // { }; 如下几种特化 // 两个模板参数具有相同的类型 template <typename T> class Myclass<T, T> // { }; // 第2个模板参数的类型是int template <typename T> class Myclass<T, int> { }; // 两个模板参数都是指针类型 template <typename T1, typename T2> class Myclass<T1*, T2*> // 也可以使引用类型T&,常引用等 { }; 创建对象 Myclass <int, float> m1; // 使用 Myclass<T1, T2> Myclass <float, float> m2; // 使用 Myclass<T, T> Myclass <float, int> m3; // 使用 Myclass<T, int> Myclass <int*, float*> m4; // 使用 Myclass<T1*, T2*> 如果创建对象时候,出现一个对象对应两个模板类,就会报错; # 4. more # ## 4.1 ## # 优点: # 1. 灵活性, 可重用性和可扩展性; 2. 可以大大减少开发时间,模板可以把用同一个算法去适用于不同类型数据,在编译时确定具体的数据类型; 3. 模版模拟多态要比C++类继承实现多态效率要高, 无虚函数, 无继承; # 缺点: # 1. 易读性比较不好,调试比较困难; 2. 模板的数据类型只能在编译时才能被确定; 3. 所有用基于模板算法的实现必须包含在整个设计的.h头文件中, 当工程比较大的时候, 编译时间较长; # 参考 # 1. 模板的优点vs 缺点:[https://www.cnblogs.com/shines77/p/3179022.html][https_www.cnblogs.com_shines77_p_3179022.html] 2. C++模板编程/泛型编程:[https://www.jianshu.com/p/18955b20d7b2][https_www.jianshu.com_p_18955b20d7b2] [format_png]: /images/20230531/e15add1569084b078a01e87036897034.png [format_png 1]: /images/20230531/d3f3d793ed7f4d24956d6ad4d71a07ba.png [https_www.cnblogs.com_shines77_p_3179022.html]: https://www.cnblogs.com/shines77/p/3179022.html [https_www.jianshu.com_p_18955b20d7b2]: https://www.jianshu.com/p/18955b20d7b2
相关 C++ 模板 C++入门系列文章: [C++、STL常用容器][C_STL] [C++、STL – 函数对象、常用算法][C_STL _] 前言: > 学习模板并不是为了写模板,而 冷不防/ 2024年03月25日 12:45/ 0 赞/ 78 阅读
相关 C++模板 C++模板 1. 模板概念 2. 函数模板 2.1 函数模板概念 2.2 函数模板格式 2.3 函数模板的原理 客官°小女子只卖身不卖艺/ 2023年09月28日 12:09/ 0 赞/ 20 阅读
相关 C++模板 1. 为什么要使用模板? 1. 假如设计一个求两参数最大值的函数,在实践中我们可能需要定义四个函数: ![format_png][] 2. 这些函数 怼烎@/ 2023年05月31日 06:56/ 0 赞/ 17 阅读
相关 C++ 模板: 函数模板 文章目录 C++ 模板 函数模板 1. 模板的概念 2. 函数模板 2.1 函数模板语法 2.2 秒速五厘米/ 2022年12月30日 12:55/ 0 赞/ 274 阅读
相关 C++模板 函数模板 函数模板,是可以创建一个通用的函数,可以支持多种形参。 用关键字 `template` 来定义, 在函数模板中,数据的值和类型都被参数化了,发生函数调用时编 你的名字/ 2022年12月23日 00:43/ 0 赞/ 125 阅读
相关 C++模板 C++模板 ①模板是实现代码重用机制的一种工具。就是根据参数类型生成函数和类的机制。它可以分成两类:一是函数模板,二是类模板,他们允许用户构造模板函数,模板类。 也可称通用 谁借莪1个温暖的怀抱¢/ 2022年09月17日 11:20/ 0 赞/ 149 阅读
相关 c++模板 1定义函数模板 include<stdexcept> include <sstream> include <map> using namesp 水深无声/ 2022年08月21日 08:55/ 0 赞/ 167 阅读
相关 C++:模板 http://[blog.csdn.net/pipisorry/article/details/72353250][blog.csdn.net_pipisorry_articl 逃离我推掉我的手/ 2022年06月16日 13:59/ 0 赞/ 198 阅读
相关 c++模板 1.类模板及其(全)特化和偏特化 模板特化是通过"给模板中的所有模板参数一个具体的类"的方式来实现的.而模板偏特化则是通过"给模板中的部分模板参数以具体的类,而留下剩余的模板 红太狼/ 2022年05月17日 03:49/ 0 赞/ 160 阅读
相关 C++模板 文一:[/images/20220319/2dfa5cca396940129714244edcacafad.png][http_www.cnblogs.com_CaiNiaoZ Bertha 。/ 2022年03月19日 13:30/ 0 赞/ 243 阅读
还没有评论,来说两句吧...