当前位置: 首页 > news >正文

C++的“模板”

前言:

为了实现泛型编程,增强代码的灵活性,对此C++通过模板来实现,在工程中熟练掌握模板的使用,是非常重要的基本功,本文对C++的模板进行了详细地讲述,方便我们学习并且熟练掌握这个语法

模板的使用:

模板的格式:在函数头或者类头部加上这个一句即可,可以用 typename或者class是用来定义模板参数关键字

template<typename T>
template<class T>
template<class T1,class T2,...>

函数模板和类模板:

函数模板:

函数模板本质是一个蓝图(模具),它不是具体的函数,根据实例化的类型,编译器才会自动生成对应的函数

实例化:

隐式实例化:让编译器根据实参推演模板参数的实际类型

如下图所示:

template<typename T>
T Add(T val1,T val2)
{return val1+val2;
}
int main()
{int a=1,b=2;Add(a,b);//通过a,b实参的int类型,推导出T为整形的Add函数double c=1.1;Add(a,c);//会有问题,函数模板中一个T推导式int,一个T推导是double,导致混乱进而报错//可以通过类型强转来实现Add((double)a,c);return 0;
}

隐式实例化无法进行隐式类型转换,这个时候除了像上述代码强转以外,还可以进行显示实例化

显示实例化: 在函数名后的<>中指定模板参数的实际类型
template<typename T>
T Add(T val1,T val2)
{return val1+val2;
}
int main()
{int a=1,b=2;Add(a,b);//通过a,b实参的int类型,推导出T为整形的Swap函数double c=1.1;//Swap(a,c);//会有问题,函数模板中一个T推导式int,一个T推导是double,导致混乱进而报错//可以通过类型强转来实现Add((double)a,c);return 0;
}

如果类型不匹配的话,会进行隐式类型转换,如果转换失败编译器则会报错

模板函数匹配规则:

1.针对存在模板函数和非模板函数同时存在,如果模板函数隐式实例化后等于非模板函数则会先调用非模板函数。如果不等于的话,则会调用更匹配的模板函数。

template<typename T>
T Add(T val1,T val2)
{cout<<"Add(T ,T)"<<endl;return val1+val2;
}
int Add(int a,int b)
{cout<<"Add(int ,int)"<<endl;return a+b;
}
int main()
{int a=1,b=2;double c=1.1,d=2.2;Add(a,b);Add(c,d);Add<int>(a,b);return 0;
}

运行结果:

2. 模板函数隐式实例化不允许类型转换,但普通函数可以进行类型转换

类模板:

类模板本质跟模板函数一样是一个蓝图(模具),它不是具体的类,根据实例化的类型,编译器才会自动生成对应的类

实例化:

类的实例化一般都是隐式实例化。

隐式实例化:类名<类型>对象名;

如下图的Test<int> t;

template<class T>
class Test
{public:Test(T a):_a(a){cout<<"Test: a"<<_a<<endl;}private:T _a;
};
int main()
{Test<int> t(10);return 0;
}

非模板类型参数:

  • 类型形参作为类型传递
  • 非类型形参作为常量使用

使用:

template<class T,size_t N=10>
class MyArry
{public:MyArry():_nums(N){cout<<"nums的size大小"<<N<<endl;}private:vector<T>_nums;
};
int main()
{MyArry<int>arr1;MyArry<int,20>arr2;return 0;
}

这里的N就是非类型形参,作为常量使用

注意:

    • 1.浮点数、类对象以及字符串是不允许作为非类型模板参数的。
    • 2. 非类型的模板参数必须在编译期就能确认结果。

模板的特化:

函数模板特化:

使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,对于这些特殊类型我们要进行特殊处理,如下文所示:

template<class T>
bool Compare(T val1,const T val2)
{return val1>val2;
}
int main()
{int a=10,b=5;int*p1= &a;int *p2=&b;cout<<Compare(a,b)<<endl;cout<<Compare(p1,p2)<<endl;return 0;
}

运行结果不符合我们预期

那么对于int*这种函数的比较,我们要单独处理,进行函数的特化

template<>
bool Compare<int*>( int*  pval1, int* pval2)
{cout<<"int*特化Compare函数"<<endl;return *pval1>*pval2;
}

进行特殊处理胡,运行结果符合预期:

对于函数来讲,一般情况下如果函数模板遇到不能处理或者处理有误的类型,一般都会单独写一个函数来解决这个问题,简单明了,可读性高,函数的特化由于受限模板参数的影响可能更繁琐

类模板特化:

同样的针对特殊类型的类,我们要特化进行特殊处理,而类的特化分为全特化和偏特化

全特化: 全特化即是将模板参数列表中所有的参数都确定化
template<class T1,class T2>
class Example
{public:Example(){cout<<"Example<class T1,class T2>"<<endl;}
};
template<>
class Example<int,char>
{public:Example(){cout<<"Example<int,char>"<<endl;}
};
int main()
{Example<int,double>e1;Example<int,char>e2;return 0;
}

运行结果符合预期:

偏特化:偏特化即是将模板参数列表中部分的参数都确定化。
template<class T1,class T2>
class Example
{public:Example(){cout<<"Example<class T1,class T2>"<<endl;}
};
template<>
class Example<int,char>
{public:Example(){cout<<"Example<int,char>"<<endl;}
};
template<class T1>
class Example<T1,int>
{public:Example(){cout<<"Example<T1,int>"<<endl;}
};
template<class T1>
class Example<T1*,int*>
{public:Example(){cout<<"Example<T1*,int*>"<<endl;}
};
int main()
{Example<int,double>e1;Example<int,char>e2;Example<char,int>e3;Example<int*,int*>e4;return 0;
}

通过偏特化,限制第二个类型形参,对于int和int*类型的都会进行特殊处理

在工程中,偏特化可以起到限制参数的作用

总结:

模板的出现让C++可以实现泛型编程,通过模板复用代码,节省资源,增强了代码的灵活性。但是模板也有缺点,如果有多个不同类型的实例化,编译器会生成很多函数代码,导致代码膨胀,使得编译器时间变长,并且如果出现了模板编译错误,不容易定位错误。

还有一个简单的思维导图,方便大家复习巩固:

结语:

以上就是我分享的C++模板的全部内容了,希望对大家有些帮助,也希望与一样喜欢编程的朋友们共进步

谢谢观看

如果觉得还阔以的话,三连一下,以后会持续更新的,我会加油的

祝大家早安午安晚安

http://www.lryc.cn/news/613100.html

相关文章:

  • day069-Jenkins基础使用与参数化构建
  • golang的面向对象编程,struct的使用
  • 急危重症专科智能体”构建新一代急诊、手术与重症中心的AI医疗方向探析
  • 【深度学习机器学习】构建情绪对话模型:从数据到部署的完整实践
  • 小鸡模拟器安卓版:经典街机游戏的移动体验
  • Elcomsoft Wireless Security Auditor 安装教程-安全检测工具使用指南
  • 数据结构----栈和队列认识
  • 深度学习-卷积神经网络CNN-1×1卷积层
  • 仓库管理系统-21-前端之入库和出库管理
  • Windows中安装rustup-init.exe以及cargo build报错443
  • 零基础-动手学深度学习-9.3. 深度循环神经网络
  • 流程图使用规范
  • Android 之 面试八股文
  • GCC与NLP实战:编译技术赋能自然语言处理
  • 解决GitHub无法打开
  • idea开发工具中git如何忽略编译文件build、gradle的文件?
  • 复杂井眼测量中,陀螺定向和磁通门定向哪个更胜一筹?
  • 幕后英雄 —— Background Scripts (Service Worker)
  • 浅析 Berachain v2 ,对原有 PoL 机制进行了哪些升级?
  • 我的世界Java版1.21.4的Fabric模组开发教程(十七)自定义维度
  • 比较一下XGBoost与LSTM、GRU、Transformer数据格式方面的核心区别
  • 零基础-动手学深度学习-9.1. 门控循环单元(GRU)及代码实现
  • Docker国内可用镜像列表(长期免费)
  • 接入小甲鱼数字人API教程【详解】
  • Next.js 样式:CSS 模块、Sass 等
  • ENSP 中静态路由负载分担
  • vue3接收SSE流数据进行实时渲染日志
  • RabbitMQ-日常运维命令
  • CS231n2017 Assignment3 RNN、LSTM部分
  • 3深度学习Pytorch-神经网络--全连接神经网络、数据准备(构建数据类Dataset、TensorDataset 和数据加载器DataLoader)