C++模板详解——使用篇
假如我们需要取得两个变量中较大的变量,或许,我们可以通过重载的方式实现,如下。
int max(int fir,int sec);
float max(float fir,float sec);
double max(double fir,double sec);
有一天,我们定义了一个新的type,School,取决于max的实现,我们不仅需要重载School::operator<(), 或者School::operator>()还要重载一个新的max
const School max(School& fir,School& sec);
使用C++的模板,从此告别这些繁琐而又略显臃肿的代码。
注:1. 上述的返回值可以考虑使用const School&,但一般不建议,参见在返回值拒绝reference
2.形参使用了School&,参见传参时,使用引用替换变量
函数模板##
顾名思义,模板,也就是“模板”,并不是实际存在的东西,而只是为了让我们更方便地生产某些东西的模具。C++的模板分为了两类,类模板与函数模板。分别用于让我们方便地“生产各种各样的函数与类”,它们都使用了template,class,typename几个关键字。为什么说是各种各样,看完了博客自然就明白了。下面介绍函数模板。
示例####
template < class type>
type max(type fir,type sec)
{
return fir > sec ? fir : sec;
}
template告诉编译器这是一个模板,紧跟在后面的<>中声明了模板形参,这些形参在模板中可以充当类型,声明可以选用class或者typename,暂时认为两者在C++中作用相同。普通函数的形参为一个变量,模板形参为一种变量类型。也就是,我们可以通过指定模板形参的类型。来个简单的例子。
比如int,float来形成不同的重载函数
template < class type...>
type max(type fir,type sec...)
{
return fir > sec ? fir : sec;
}
void main()
{
int a(1),b(2);
float c(1),d(2);
max(a,b); //具现化int max(int fir,int sec);
max(c,d); //具现化int max(float fir,float sec);
}
第一个max使用了int类型的参数,相当于告诉函数模板,type对应于int,在具现化的函数模板中,type的作用相当于int。所以具现化的函数相当于int
max(int fir,int sec);相对应地,使用了float调用函数模板,也就是制定了type为float,与前一个函数形成了重载。
注:虽然float能够隐私转换为int,但是还是会具现化新的函数。只有当前的参数类型与已经具现化的函数模板完全匹配的时候,才会继续使用已经具现化的函数。
拓展####
template < class type_1,class type_2...>
type_1 func(type_2 fir,type_1 sec,int thir)
{
//return...
}
相对于前一个模板函数,这个模板函数的模板形参数量增加了,在普通的形参列表中,模板形参的顺序打乱了,还增加了int的形参。
- 在模板形参中,我们可以随意地定义任意数量的模板形参,但必须保证能够全部初始化。
- 使用了不同的类型名type_1,type_2...意味着我们可以指定多种类型的模板形参,其类型也可以不相同。
- 模板形参没有要求必须与普通函数形参一一对应,即在形参中的顺序可以随意打乱,其类型由相应的普通形参的类型决定。如,type_1的类型由sec的类型决定。
- 在模板函数中,除了模板形参外,可以使用内置的或者自定义的类型。
还是来个简单的例子
template < class type_1,class type_2>
void max(type_2 fir,type_1 sec,int)//最后的参数没有使用,可以直接忽略形参名
{
std::cout<<fir<<"+"<<sec<<endl;
}
void main()
{
int a(1);
float b(1.0);
max(a,b,1); //1. 具现化void max(int fir,float sec,int);
}
第一个实参为int型,其对应的形参是type_2,所以type_2具现化后就是int。
第二个实参为float,其对应的形参type_1,所以type_1具现化后就是float。最后的具现化的函数就是int max(int fir,float sec,int);
指定参数类型####
还记得使用STL容器的方法吗,比如定义一个vector类型的容器。STL也叫作标准模板库,也就是其内部也是通过模板实现的,所以这种名称后加类型名的方法对我们也同样适用。
void Select(int a)
{
std::cout<<"是int型"<<endl;
}
void Select(float a)
{
std::cout<<"是float型"<<endl;
}
template < class type_1,class type_2>
void myPrint(type_1 fir,type_2 sec)
{
Select(fir);
}
void main()
{
myPrint(1.0,1); //输出"是float型"
myPrint<int>(1.0,1); //输出"是int型"
}
在上面的例子中,我们可以发现:
- 在调用模板函数的时候,我们可以通过直接指定模板形参的类型从而阻止普通函数形参对于模板形参的影响。但是,指定的类型与普通函数形参必须能够进行类型转换。
比如,内置类型的int与double可以相互转换,所以myPrint< double>(1)可用。但是string与int之间不可相互转换myPrint< string>(1)就没办法通过编译。假如我们定义了class My,其构造函数为public:My(int),那么认为My与int可以相互转换(本质上是隐式调用了My的构造函数),myPrint< My>(1)就可以通过编译。
类模板##
假如你对函数模板还不会使用,请自行回顾,一些函数模板讲过的在下面不再赘述。
实例##
template < class type_1,class type_2>
class Student
{
public:
Student(){}
Student(type_1 fir,type_2 sec){}
Student(type_1 fir){}
private:
type_1 value_1;
type_2 value_2;
...
};
void main()
{
Student stu(1,1); //error
Student<int,float> stu(1,1); //OK
}
template,class的作用与函数模板一致。不同的是:
- 类模板必须在使用的时候指定好模板形参的类型,编译器不会通过public接口,包括构造函数去作为模板形参类型的辨别依据。记得vector vec吧,没见过vector vec(1)吧。
- 使用类模板的时候,使用到的成员函数在主调语句必须可见。比如,上述的Student(type_1 fir,type_2 sec)在main中调用,其函数定义在main所在文件必须可见。再比如上述例子,假如其实现分配到如下几个文件,在链接的时候将出错。读者可以先记得,在“精通篇”会详细阐述这一点。
- 类模板中,慎用模板形参重载函数。上述的例子中,假如再增加Student(type_2)就会编译出错。编译器无法在Student(type_1)与Student(type_2)中做抉择。
//1.h
template < class type_1,class type_2>
class Student
{
public:
...
Student() //有具体实现的构造函数
{
...
}
Student(type_1 fir,type_2 sec);
private:
...
};
//1.cpp
#include"1.h"
Student< class type_1,class type_2>::Student()
{}
//core.cpp
#include"1.h"
void main()
{
Student<int,int> stu(1,1); //构造函数定义在1.cpp中,不可见,出错
Student<int,int> stu(); //默认构造函数随1.hinclude,可见,编译通过
}
C++中模板的基本使用方法如上。下一篇博客将带大家进入模板特化以及深入解释上述例子无法编译的原因。
C++模板详解——使用篇的更多相关文章
- C++ 类模板详解(一):概念和基本使用方式
与函数模板类似地(C++函数模板详解(一):概念和特性) ,类也可以被一种或多种类型参数化.例如,容器类就是一个具有这种特性的典型例子,它通常被用于管理某种特定类型的元素.只要使用类模板,我们就可以实 ...
- C++模板详解(三):参数化声明详解
在前两节中(C++模板详解(一).C++模板详解(二)),我们了解了函数模板和类模板的基本概念和使用方法.在这篇博文里,我们主要来详细地阐述一下"模板的参数声明"这个话题,并且也谈 ...
- vue2.x版本中computed和watch的使用入门详解-computed篇
前言 在基于vue框架的前端项目开发过程中,只要涉及到稍微复杂一点的业务,我们都会用到computed计算属性这个钩子函数,可以用于一些状态的结合处理和缓存的操作. 基础使用 在computed中,声 ...
- 微信授权步骤与详解 -- c#篇
微信授权步骤与详解 -- c#篇 注:这里不涉及界面操作,只介绍代码操作. 1.基本原理如下: 从图上所知,第一步用户访问我们的网页,第二步我们后台跳转到微信授权页面,第三步用户点击授权,第四步微信重 ...
- bt协议详解 DHT篇(下)
bt协议详解 DHT篇(下) 最近开发了一个免费教程的网站,产生了仔细了解bt协议的想法,这篇文章是bt协议详解系列的第三篇,后续还会写一些关于搜索和索引的东西,都是在开发这个网站的过程中学习到的技术 ...
- bt协议详解 DHT篇(上)
bt协议详解 DHT篇(上) 最近开发了一个免费教程的网站,突然产生了仔细了解bt协议的想法,这篇文章是bt协议详解系列的第三篇,后续还会写一些关于搜索和索引的东西,都是在开发这个网站的过程中学习到的 ...
- IIS负载均衡-Application Request Route详解第二篇:创建与配置Server Farm(转载)
IIS负载均衡-Application Request Route详解第二篇:创建与配置Server Farm 自从本系列发布之后,收到了很多的朋友的回复!非常感谢,同时很多朋友问到了一些问题,有些问 ...
- IIS负载均衡-Application Request Route详解第一篇: ARR介绍(转载)
IIS负载均衡-Application Request Route详解第一篇: ARR介绍 说到负载均衡,相信大家已经不再陌生了,本系列主要介绍在IIS中可以采用的负载均衡的软件:微软的Applica ...
- C++模板详解
参考:C++ 模板详解(一) 模板:对类型进行参数化的工具:通常有两种形式: 函数模板:仅参数类型不同: 类模板: 仅数据成员和成员函数类型不同. 目的:让程序员编写与类型无关的代码. 注意:模板 ...
随机推荐
- Python3基础 file with 配合文件操作
Python : 3.7.0 OS : Ubuntu 18.04.1 LTS IDE : PyCharm 2018.2.4 Conda ...
- 安装PyInstaller打包python
安装PyInstaller 对于那些网络比较稳定,能够流畅使用pip源地址的用户,直接下面的命令就可以搞定: pip install pyinstaller 通常我们会下载源码包,然后进入包目录,执行 ...
- 【传输对象】kafka传递实体类消息
工具类 负责对象字节数组的相互转换,传输数据用 package com.yq.utils; import java.io.ByteArrayInputStream; import java.io.By ...
- C++快速输入输出优化
在这里存一下我的快速输入输出优化 以及写题模板 这里的是$getchar$优化和$putchar$优化,$fread$和$fwrite$暂时咕咕咕 快速输入 这里$define$了一个$I\_int$ ...
- IDEA快捷键复习使用
https://www.jetbrains.com/help/idea/meet-intellij-idea.html 快捷键可以极快地进行代码编辑整理,在IDEA的快捷键中,除了有几个好像特别难按之 ...
- C#学习笔记(十七):委托、事件、观察者模式、匿名委托和lambert表达式
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- C#学习笔记(八):多维数组
一维数组 冒泡排序 二维数组 Length:取数组元素的总个数 GetLength:取不同维度的个数 using System; using System.Collections.Generic; u ...
- CSAPP学习笔记 第一章 计算机系统漫游
Ch 1.0 1.计算机系统是由硬件和系统软件组成的 2.本书阐述了计算机组件是如何工作的以及执行组件是如何影响程序正确性和性能的. 3.通过跟踪hello程序的生命周期来开始对系统的学习. #inc ...
- ros 使用命令测试topic
发布话题 $ rostopic pub -r /chatter std_msgs/String "test" 输出数据: $ rostopic echo /chatter data ...
- ubuntu 14.04 server(amd64) 安装ros indigo
1.添加软件源(添加了正确的软件源,操作系统就知道去哪里下载程序,并根据命令自动安装软件) sudo sh -c 'echo "deb http://packages.ros.org/ros ...