C++模板到底是个啥,看这就透了

Posted 两片空白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++模板到底是个啥,看这就透了相关的知识,希望对你有一定的参考价值。

 目录

前言

一.定义

 二.函数模板

2.1 概念

2.2 函数模板格式

2.3 函数调用的原理

2.4 函数模板的实例化

 2.5 函数模板的匹配规则

三.类模板

3.1 类模板格式

3.2 类模板的实例化 

3.3 类的函数模板


前言

        我们知道C++相对于C语言新增了一个功能函数重载,简单来说函数重载是对于类型不同的参数,可以存在相同的函数名,编译器会通过实参类型来调用相应的函数。

但是,这样会有几个不大实用的地方。

  1. 重载函数只是参数类型不同,代码的复用率比较低,只要由新的类型出现,就需要增加对应的函数。
  2. 代码的可维护性比较低,一个出错可能所有重载都会出错。

为了很好的解决上面的问题,于是C++增加了一个新的功能,叫模板。

一.定义

        想要了解模板,首先我们来了解一下泛型编程。

        泛型编程:编写与类型无关的通用代码,是代码复用的一种手段,是针对广泛类型,与类型无关。模板是泛型编程的基础。

在编写代码时,一些函数和类,针对不同的类型需要些很多重复的代码。

        函数比如我们要编写一个交换函数swap,当要交换int,double,char类型时,我们就需要编写三个重载函数。

        类,比如我们想实现数据结构栈stack时,比如定义多个对象st1和st2,st1数据要存储int类型,而st2要存储double类型时,我们就需要写两个栈。

        模板就是针对类型不同而逻辑相同的函数或类,编写一个模板(具体如何编写,下面由有代码),于是我们用户就不用因为类型不同,要编写多个函数或者类。编译器会通过模板生成对应的函数或类。

因此模板分为两类

 二.函数模板

2.1 概念

        函数模板就好像代表了一个函数的家族。该函数模板与类型无关,在使用时被参数化,根据实参类型产生对应类型的函数。

2.2 函数模板格式

        函数模板使用到关键字template

例如写一个交换函数: 

template<class T>
void Swap(T& x, T& y){
	T temp = x;
	x = y;
	y = temp;
}

 注意:

  1. 模板并不是函数,函数调用时,调用的也不是模板,而是模板对应生成的函数。并且类型不同参数调用时,调用的也不是同一个函数。
  2. typename时定义模板参数的关键字,也可以使用class,但是不能使用struct。

2.3 函数调用的原理

        函数模板本身并不是一个函数,是编译器通过模板来产生特定类型的具体函数。也就是本来是有我们来编写不同类型参数的函数的,现在我们通过模板将其交给了编译器来做。

        原理是:我们写了一个模板,编译器通过模板实例化出对应函数和类。

        用交换函数举例:

         在编译器编译阶段,对于函数模板的使用,编译器要通过传入实参类型来生成对应类型的函数来进行调用。这也说明在预处理阶段编译器就会生成对应的函数。简单来说,模板就是将T确定为对应传入参数的类型,T修饰的变量或对象就是传入参数的类型。

2.4 函数模板的实例化

        用不同类型的参数使用模板,编译器推演得到对应函数叫做函数模板的实例化。

        实例化分为:隐式实例化和显示实例化

  • 隐式实例化:让编译器根据实参推演模板参数的实际类型。T的类型是编译器推到的。
#include<iostream>
using namespace std;

template<class T>
T Add(const T& x, const T& y){
	return x + y;
}

int main(){
	int a = 2, b = 9;
    //没有明确给出参数类型
	Add(a, b);

	double x = 1.1, y = 2.2;
	Add(x, y);

	return 0;
}

  •  显示实例化:在函数名后的<>中指定模板参数的实际类型。T就是<>里的类型。
    #include<iostream>
    using namespace std;
    
    template<class T>
    T Add(const T& x, const T& y){
    	return x + y;
    }
    
    int main(){
    	int a = 2, b = 9;
        //参数显示给出
    	Add<int>(a, b);
    
    	double x = 1.1, y = 2.2;
    	Add<double>(x, y);
    
    	return 0;
    }
#include<iostream>
using namespace std;
//通用加法函数
template<class T1,class T2>
T1 Add(const T1& x, const T2& y){
	return x + y;
}

int main(){

	Add<int,double>(a, x);

	return 0;
}

注意在隐式实例化的情况下,如果有多个实参但是类型不同,但是模板中只有一个参数T,此时编译会报错。因为编译器无法确定T为什么类型。

#include<iostream>
using namespace std;

template<class T>
T Add(const T& x, const T& y){
	return x + y;
}

int main(){
	int a = 2;
	double x = 1.1;
    //a和x参数不同,模板中只有一个参数T
    //编译报错
	Add(a, x);
   //解决:1.强转类型转化,2.显示实例化
    Add(a,(int)x);
    Add<int>(a,x);

	return 0;
}

 2.5 函数模板的匹配规则

  • 一个非模板函数可以和一个同函数名的模板同时存在,并且该函数模板还可以实例化为这个非模板函数。
#include<iostream>
using namespace std;

//专门处理int类型加法函数
int Add(const int& x, const int& y){
	return x + y;
}

//通用加法函数
template<class T>
T Add(const T& x, const T& y){
	return x + y;
}

int main(){
	Add(1, 2);//调用非模板函数
	//<int>调用函数不这样写,这样写就是调用模板
	Add<int>(1, 2);//调用编译器通过模板推演出来的函数

	return 0;
}
  • 对于非模板函数和同名函数模板,如果其它条件相同,在调用时会优先调用非模板函数,而不会从该模板产生出一个实例。如果模板可以产生一个更好匹配的函数,那么将选择该模板。
#include<iostream>
using namespace std;

//专门处理int类型加法函数
int Add(const int& x, const int& y){
	return x + y;
}

//通用加法函数
template<class T1,class T2>
T1 Add(const T1& x, const T2& y){
	return x + y;
}

int main(){


	//与非模板函数完全匹配,不需要编译器通过模板推演出函数
	Add(1, 2);

	int a = 2;
	double x = 1.1;
	//非模板函数不适用,调用模板函数
	Add(a, x);

	return 0;
}
  • 模板函数不允许自动类型转化,当普通函数可以自动类型转化。

三.类模板

3.1 类模板格式

        

 定义一个学生类:

template<class T1,class T2,class T3>
class Student{
public:
//构造函数
 Student(T1 name,T2 age,T3 score){
    _name=name;
    _age=age;
    _score=score;
 }
private:
 T1 _name;
 T2 _age;
 T3 _score;
}

3.2 类模板的实例化 

类的实例化与函数实例化不同,类实例化必须显示实例化。即类实例化时必须在类名后面跟<>,然后将类型放在<>里。

类模板并不是真正的类,实例化出来的才是真正的类。

//student是类名,student<string,int,float>才是类型
Student<string,int,float>s("Tom",18,85.5);

 student是类名,student<string,int,float>才是类型

3.3 类的函数模板

以上是关于C++模板到底是个啥,看这就透了的主要内容,如果未能解决你的问题,请参考以下文章

C++ 适配器到底是个啥概念, 函数适配器,迭代器适配器

吵疯了,Pull Request到底是个啥?

话说Spring 5里的WebFlux到底是个啥?

linux指令 2>&1 到底是个啥

一文带你搞懂RPC到底是个啥

NoSQL到底是个啥?