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++ 模板详解(一) 模板:对类型进行参数化的工具:通常有两种形式: 函数模板:仅参数类型不同: 类模板: 仅数据成员和成员函数类型不同. 目的:让程序员编写与类型无关的代码. 注意:模板 ...
随机推荐
- C++ 表示一个区间值得方法
C++中不允许这样的写法 85<= score <=100;你要想表示85<=score<=100的话只能这么写score>=85&&score<= ...
- gvim写html代码时如何快速地跳转到一个标签的结束位置: 终极插件: matchit.vim
gvim写html代码时如何快速地跳转到一个标签的结束位置 参考这个vimrc的配置, 里面有一些 很好的东西, 配置很有用, 以前没有用到: http://www.cnblogs.com/wangj ...
- Node10.15.0的安装
1. 首先我们需要去node官网下载最近版本的压缩包,然后我们把他们解压到我们自定义的安装路径,我使用的是/usr/local/lib/nodejs VERSION=v10.15.0 DISTRO=l ...
- .Net Core集成Office Web Apps(二)
想要使用OWA需要一台单独的服务器来部署,这对很多人造成困难.而写该文的目的是为了分享有个OWA的集成步骤,它不仅适用于.Net开发环境,其它语言也是一样的,只要实现了需要的服务接口.并且该文不局限与 ...
- 高精度N的阶乘-N!
题目:输入一个正整数N(0<N<=30),求N! 代码: #include<stdlib.h> #include<cstdio> #include<strin ...
- POJ 3628 Bookshelf2(0-1背包)
http://poj.org/problem?id=3628 题意:给出一个高度H和n个牛的高度,要求把牛堆叠起来达到H,求出该高度和H的最小差. 思路:首先我们计算出牛的总高度sum,sum-H就相 ...
- c++ 查找容器中不满足条件的元素,返回iterator(find_if_not)
#include <iostream> // std::cout #include <algorithm> // std::find_if_not #include <a ...
- MongoDB(课时14 正则运算)
3.2.4.9 正则运算 如果想实现模糊查询,必须使用正则表达式,而且正则表达式使用的语言是Perl兼容的正则表达式的形式. 要实现正则使用,则按照如下的定义格式: 基础语法:{key : 正则标记} ...
- TPCx-BB源码分析
Copy from: 一篇文章看懂TPCx-BB(大数据基准测试工具)源码 TPCx-BB是大数据基准测试工具,它通过模拟零售商的30个应用场景,执行30个查询来衡量基于Hadoop的大数据系统的包括 ...
- Mac下使用源码编译安装TensorFlow CPU版本
1.安装必要的软件 1.1.安装JDK 8 (1)JDK 8 can be downloaded from Oracle's JDK Page: http://www.oracle.com/techn ...