C++函数模板

最近学习了C++的函数模板,来做一做小笔记.

一、为什么要使用函数模板?

1.提高代码的复用性

        函数模板可以用来创建一个通用的函数,适用于多种不同的数据类型。

例如我们可以编写一个比较两个数大小的函数模板,然后这个模板就可以用于比较整数,浮点数,字符等多种类型的数据,这样就不用为每一种数据类型都分别写一个比较数大小的函数了。

2.增强程序的灵活性和可维护性

        当需要修改函数的逻辑时,如果用了函数模板我们就只需要修改函数模板的定义,然后所有基于该模板生成的具体函数都会随之改变。这样可以减少代码量,而且使代码的维护变得更加方便。

 

二、函数模板的声明与定义

函数模板的声明:

        函数模板声明以关键字 template 开头,后面接模板参数列表(用尖括号 <> 包围),参数列表中可以包含一个或多个模板参数,这些参数可以是类型参数(通常用 typename class 关键字开头)或非类型参数(如整数常量等).例如,一个简单的交换两个变量的函数模板声明如下:

template<typename T>

void Swap(T& a,T& b);

这里typename T 声明了一个类型参数T,函数Swap用于交换两个类型为T的变量的值。

 

函数模板的定义:

        它定义的方式和声明类似,也是以template关键字和模板参数列表开头,然后是函数体。例如,上面 Swap 函数模板的定义如下:

        template<typename T>

        void Swap(T& a , T& b){

                T temp = a;

                a = b;

                b = temp;

        }

在函数体内部,可以像使用普通类型一样使用模板参数所代表的类型。这样,当在程序中调用这个函数模板时,编译器会根据实际的参数类型自动生成对应的函数实例.

但一般在C++中模板的声明和定义都是放在头文件中。因为编译器在编译调用模板函数的代码时,需要看到完整的模板定义才能进行实例化。

总结:  在C++中,template是一个关键字,用来声明这是一个模板. <typename T>或<class T>是模板参数列表,T是一个类型参数,在函数定义中可以像使用普通类型一样使用它。函数模板的主要优势是可以编写一个通用的函数逻辑,适用于多种数据类型。


三、函数模板的调用机制:

        以下是一个简单的函数模板示例,用于交换两个变量的值:

//函数模板定义

template<typename T>

void Swap(T &a, T &b){

        T temp = a;

        a = b;

        b = temp;

}

        下面是调用这个函数模板的示例:

#include <iostream>

int main(void){

int num1 = 5;

int num2 = 10;

 

std::cout<<"Before Swap:num1 = "<<num1<<" num2 = " << num2 <<std::endl;

//编译器自动推导T为int 类型并实例化函数

Swap(num1,num2);

std::cout<<"After Swap:num1 = "<<num1<<" num2 = " << num2 <<std::endl;

 

double d1 = 3.14;

double d2 = 6.28;

 

std::cout<<"Before Swap:d1 = " <<d1 <<",d2 = "<< d2 <<std::endl;

//编译器自动推导T为double类型并实例化函数

Swap(d1,d2);

std::cout<<"After Swap:d1 = " <<d1<<",d2 = "<<d2 <<std::endl;

 

return 0;

}

在这个例子中,Swap是一个函数模板。

在main函数里面第一次调用Swap(num1,num2)时,编译器根据实参num1与num2的类型(int)推到出模板函数T为int,然后实例化一个void Swap(int &,int &)函数来完成交换操作。第二次调用Swap(d1,d2)时,编译器推导出T为double类型,并实例化void Swap(double &,double &)函数来交换两个double类型变量的值。

若是想要显式置顶模板参数类型,也可以这样修改:

int main(){

int num1 =5;

int num2 =10;

 

std::cout<<"Before Swap:num1 = " << num1 <<",num2 = "<<num2<<std::endl;

//显式指定T为int类型

Swap<int>(num1,num2);

Std::cout<<"After Swap:num1 = "<< num1 << ",num2 = "<<num2 <<std::endl;

 

return 0;

}

这种显式指定在某些复杂情况下(比如模板参数推到可能出现歧义时)很有用。

 

四、使用函数模板的易错点

以下是使用函数模板时的一些易错点:
 
1.类型推导
 
- 编译器根据传递给函数模板的实参来推导模板参数的类型,但这个推导过程可能会出现意外结果。例如,当函数模板参数是一个数组时,实际上推导出来的类型是指针类型。像 template<typename T> void func(T param); 如果调用 func("hello") ,这里 T 会被推导为 const char* ,而不是 const char[6] 


- 对于引用类型,在推导过程中需要注意。如果传递一个常量引用,模板参数类型会被推导为常量引用类型,如 template<typename T> void func(T& param); ,当调用 func(const int& num) 时, T 会被推导为 const int 。
 
2.模板参数匹配
 
- 函数模板可能会和普通函数重载在一起,当有多个匹配的函数或函数模板时,编译器的选择规则比较复杂。一般来说,非模板函数优先于模板函数被调用,如果有多个模板函数都能匹配,会选择最特化的那个。例如,有一个普通函数 int add(int a, int b) 和一个函数模板 template<typename T> T add(T a, T b) ,当调用 add(1, 2) 时,会优先调用普通函数而不是模板函数。


- 当函数模板有多个模板参数,在调用时要确保传递的实参能够正确匹配每一个模板参数的类型,避免出现类型不匹配的错误。
 
3.编译错误提示模糊
 
- 由于函数模板在实例化时才会进行真正的类型检查,所以错误信息可能会很复杂和模糊。比如模板函数体内部使用了某个成员函数,但对于某些模板参数类型没有这个成员函数,在编译错误提示中可能会出现大量模板相关的复杂信息,很难直接定位到问题的核心是成员函数不存在。

 

The end......(有问题还望大佬们指出 #w#)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值