模板与泛型编程1(函数模板)

定义、实例化函数模板:

对于函数体完全相同,唯一差异就是参数类型的情况,我们可以定义一个通用的函数模板,而非为每个类型都定义一个新函数:

 1 #include <iostream>
 2 #include <vector>
 3 using namespace std;
 4
 5 template <typename T>//模板参数列表
 6 int compare(const T &v1, const T &v2) {
 7     if(v1 < v2) return -1;
 8     if(v2 < v1) return 1;
 9     return 0;
10 }
11
12 int main(void) {
13     cout << compare(1, 0) << endl;//T为int
14     cout << compare(vector<int>{1, 2, 3}, vector<int>{2, 3, 4}) << endl;//T为vector<int>
15
16     return 0;
17 }

注意:模板定义以关键字 template 开始,后跟一个模板参数列表,里面有一个或多个模板参数在模板定义中,模板参数列表不能为空

类似于函数参数,模板参数表示在类或函数定义中用到的类型或值。当使用模板时,我们 (隐式的或显式地) 指定模板实参,将其绑定道模板参数上。

当我们调用一个函数模板时,编译器(通常)用函数实参来为我们推断模板实参,并用推断出的模板参数来为我们实例化一个特定版本的函数。

模板参数可以是模板类型参数或非类型模板参数(表示值而非类型)

模板类型参数:

前面的 compare 中 T 就是一个模板类型参数。一般来说我们可以将类型参数看作类型说明符,就像内置类型或类类型说明符一样使用

1 template <typename T>//类型参数
2 //函数模板返回类型与参数类型相同
3 T foo(T *p) {//类型参数可硬用来指定返回类型或函数的参数类型
4     T tmp = *p;//类型参数可以用于函数体内部声明变量或类型转换
5     //...
6     return tmp;
7 }

注意:类型参数可用来指定返回类型、函数的参数类型、声明变量或类型转换

类型参数前必须使用关键字 class 或 typename:

// 错误,U 之前必须加上 class 或 typename

template <typename T, U> T calc(const T&, const U&);

//正确

template <typename T, class U> T calc(const T&, const U&);

注意:在模板参数列表中,class 和 typename 含义完全相同,可以互换使用

非类型模板参数:

一个非类型模板参数表示一个值而非一个类型。我们通过一个特定的类型名而非关键字 class 或 typename 来指定非类型参数。当一个模板呗实例化时,非类型参数被一个用户提供的或编译器推断出的值所代替,这些值必须是常量表达式,从而允许编译器在编译时实时实例化模板:

 1 #include <iostream>
 2 #include <string.h>
 3 using namespace std;
 4
 5 template<unsigned N, unsigned M>
 6 int compare(const char (&p1)[N], const char (&p2)[M]) {//数组不能拷贝,可以引用
 7     return strcmp(p1, p2);
 8 }
 9
10 int main(void) {
11     compare("hi", "mom");
12
13     return 0;
14 }

编译器会实例化出如下模板:

int compare(const char (&p1)[3], const char (&p2)[4])

注意:一个非类型参数可以是一个整型,或者是一个指向对象或函数类型的指针或(左值)引用

绑定到非类型整型参数的实参必须是一个常量表达式。绑定到指针或引用非类型参数的实参必须具有静态的生存期。指针参数也可以用 nullptr 或一个值为 0 的常量表达式来实例化

inline 和 constexpr 的函数模板:

template <typename T> inline T min(const T&, const T&);
注意:inline 或 constexpr 说明符放在模板参数列表之后,返回类型之前

模板编译:

当编译器遇到一个模板定义时,它并不生成代码。只有当我们实例化(使用)模板的特定版本时,编译器才会生成代码。

通常,当我们调用一个函数时,编译器只需要掌握函数的声明。类似的,当我们使用一个类型的对象时,类定义必须是可用的。但成员函数的定义不必已经出现。因此我们可以将类定义和函数声明放在头文件中,而普通函数和类的成员函数的定义放在源文件中。

模板则不同:为了生成一个实例化版本,编译器需要掌握函数模板或类模板成员函数的定义。因此,与非模板代码不同,模板的头文件通常既包括声明也包括定义。

大多数编译错误发生在实例化期间报告:

如,前面的 compare 函数,如果我们用一个没有定义 < 运算符的类型实例化该函数模板,则会发生编译错误

定义 find 模板:

 1 #include <iostream>
 2 #include <vector>
 3 #include <list>
 4 using namespace std;
 5
 6 template<typename T, typename U>
 7 T find(const T &bg, const T &ed, const U &val) {
 8     auto cnt = bg;
 9     while(cnt != ed) {
10         if(*cnt == val) break;
11         ++cnt;
12     }
13     return cnt;
14 }
15
16 int main(void) {
17     vector<int> vt = {1, 2, 3, 4, 5};
18     list<string> lt = {"jhhg", "fjsl", "zzz", "jfl"};
19
20     auto cnt = find(vt.begin(), vt.end(), 1);
21     cout << *cnt << endl;
22
23     auto gg = find(lt.begin(), lt.end(), "zzz");
24     cout << *gg << endl;
25
26     gg = find(lt.begin(), lt.end(), "aa");
27     if(gg == lt.end()) cout << "//" << endl;
28
29     return 0;
30 }

编写接受一个数组引用参数的 print 模板:

 1 #include <iostream>
 2 using namespace std;
 3
 4 template<typename T, unsigned N>
 5 void print(const T (&t)[N]) {
 6     for(int i = 0; i < N; i++) {
 7         cout << t[i] << " ";
 8     }
 9     cout << endl;
10 }
11
12 int main(void) {
13     int a[10] = {2, 2, 3};
14     print(a);
15
16     string b[3] = {"fsl", "fj", "z"};
17     print(b);
18
19     // char *ch = "jfk";//注意,不能传指针,只能传数组
20     print("fjdl");
21
22     return 0;
23 }

定义接受一个数组实参的 begin、end 模板:

 1 #include <iostream>
 2 #include <algorithm>
 3 using namespace std;
 4
 5 template<typename T, unsigned N>
 6 T* begin_(const T (&a)[N]) {
 7     return const_cast<T*>(a);//底层const只能显式的去除
 8 }
 9
10 template<typename T, unsigned N>
11 T* end_(const T (&a)[N]) {
12     return const_cast<T*>(a) + N;
13 }
14
15 template<typename T, unsigned N>
16 void print(const T (&t)[N]) {
17     for(int i = 0; i < N; i++) {
18         cout << t[i] << " ";
19     }
20     cout << endl;
21 }
22
23 int main(void) {
24     int a[10] = {7, 1, 3, 3};
25     sort(begin_(a), end_(a));
26     print(a);
27
28     return 0;
29 }

编写一个 constexpr 模板,返回给定数组的大小:

 1 #include <iostream>
 2 using namespace std;
 3
 4 template<typename T, unsigned N>
 5 constexpr unsigned size(const T (&t)[N]) {
 6     return N;
 7 }
 8
 9 class gel{
10     int x;
11 };
12
13 int main(void) {
14     gel g[10];
15     cout << size(g) << endl;
16
17     return 0;
18 }

原文地址:https://www.cnblogs.com/geloutingyu/p/8476763.html

时间: 2024-08-04 02:55:52

模板与泛型编程1(函数模板)的相关文章

C++提高1 【泛型编程】函数模板 类模板

[本文谢绝转载] [泛型编程] 函数模板 为什么会有函数模板 现象: 函数的业务逻辑一样 函数的参数类型不一样 [最常用]函数模板  显式的调用 [不常用]类型推导 多个参数,参数定义了必须要用 函数模板,实现int类型数组,char字符串排序: 函数模板 与 普通函数的本质区别 函数模板 和 普通函数在一起 的调用型研究: C++是如何支持函数模板机制的? 函数模板机制结论 类模板 类模板的定义 类模板做函数的参数 类模板的派生成普通类 模板类的派生成模板类 复数类,所有函数都写在类的内部,运

25.C++- 泛型编程之函数模板(详解)

1)初探函数模板 2)深入理解函数模板 3)多参函数模板 4)重载函数模板 当我们想写个Swap()交换函数时,通常这样写: 但是这个函数仅仅只能支持int类型,如果我们想实现交换double,float,string等等时,就还需要从新去构造Swap()重载函数,这样不但重复劳动,容易出错,而且还带来很大的维护和调试工作量.更糟的是,还会增加可执行文件的大小. 函数模板 一种特殊的函数,可通过不同类型进行调用 函数模板是C++中重要的代码复用方式 通过template关键字来声明使用模板 通过

泛型编程之函数模板

>模板的引入,为什么要使用模板? 在程序设计中往往存在这样一种现象:两个或多个函数的函数体完全相同,差别仅在于他们的参数类型不同,就需要分别给不同的数据类型定义不同的版本. 解决以上问题的一个比较好的方法就是使用模板.模板是实现代码重用机制的一种工具,他可以实现类型参数化,即把类型定义为参数,从而实现代码复用. >模板的分类: 模板分为函数模板和类模板.他们分别允许用户构造模板函数和模板类. >函数模板: 建立一个通用函数,其函数返回类型和形参类型不确定,用一个虚拟的类型来代表,这个通用

模板(1)函数模板

1.引入 如何编写一个通用加法函数?第一个方法是使用函数重载, 针对每个所需相同行为的不同类型重新实现这个函数.C++的这种编程机制给编程者极大的方便,不需要为功能相似.参数不同的函数选用不同的函数名,也增强了程序的可读性.简单示例: 1 int Add(const int &_iLeft, const int &_iRight) 2 { 3 return (_iLeft + _iRight) ; 4 }f 5 loat Add(const float &_fLeft, const

求变量的数据类型,typeid,bool,C和C++的不同,new和delete,C++中的枚举,inline和可变参数模板,auto和函数模板,宽字符

求变量的数据类型,通过函数typeid(变量名).name();获得变量的数据类型. 案例如下: #include <iostream> #include <stdlib.h> void main() { double db = 10.9; double *pdb = &db; auto num = pdb; //通过typeid的方式获得数据类型 std::cout << typeid(db).name() << std::endl; std::c

C++泛型编程之函数模板

泛型语义 泛型(Generic Programming),即是指具有在多种数据类型上皆可操作的含意.泛型编程的代表作品 STL 是一种高效.泛型.可交互操作的软件组件. 泛型编程最初诞生于 C++中,目的是为了实现 C++的 STL(标准模板库).其语言支持机制就是模板(Templates). 模板的精神其实很简单:类型参数化(type parameterized),即,类型也是一种参数,也是一种静多态. 换句话说, 把一个原本特定于某个类型的算法或类当中的类型信息抽掉,抽出来做成模板参数. 函

C++模板编程 - 第二章 - 函数模板

模板被编译两次 书上说模板被编译两次:一次是实例化之前,检查模板代码本身,这个好理解,就是检查和模板没有关系的代码:第二次编译是在实例化的时候,看看把类型带入进去有没有问题,比如说字符串没有除法. 还有一个需要注意的问题:模板在进行实例化的时候,编译器要能够看到模板的定义.下面的代码会挂掉,因为main.cpp把max.h包含进来,但是其中没有max的实现 1 // max.h 2 template<typename T> 3 max..... 4 5 // max.cpp 6 #includ

C++笔记(7):泛型编程和模板(函数模板和类模板)

泛型编程和模板 0.泛型编程 1.函数模板 2.类模板 ----------------------------------------------------------------------------------------------------------- 0.泛型编程 所谓泛型就是以独立于任何特定类型的方式编写代码.前面介绍的标准库的容器.迭代器和算法都是泛型编程的具体应用. 模板是泛型编程的基础.使用模板的时候不需要知道模板是如何定义的,但今天我们来介绍如何定义自己的模板类和模

【C/C++学院】(11)泛型编程/函数模板/类模板

1.泛型编程基础 #include "iostream" using namespace std; void swap(int &a, int &b) { int c; c = a; a = b; b = c; } void swap(float &a, float &b) { float c; c = a; a = b; b = c; } void main() { int a = 1, b = 2; swap(a, b); float a1 = 1,

C++中模板与泛型编程

目录 定义一个通用模板 模板特化和偏特化 模板实例化与匹配 可变参数模板 泛型编程是指独立与任何类型的方式编写代码.泛型编程和面向对象编程,都依赖与某种形式的多态.面向对象编程的多态性在运行时应用于存在继承关系的类,一段代码可以可以忽略基类和派生类之间的差异.在泛型编程中,编写的代码可以用作多种类型的对象.面向对象编程所依赖的多态性称为运行时多态性,泛型编程所依赖的多态性称为编译时多态性或参数式多态性. 1 模板定义 1.1 函数模板 模板定义以关键字 template 开始,后接模板形参表,模