c++ 函数模版

目录

1、模板的概念:

2、使用方法

2.1让编译器自动类型推导:

2.2显式指定数据类型:

3、函数模板的注意事项

3.1函数模板中的通用数据类型要唯一

3.2函数模板的隐式转换问题

3.3函数模板和普通函数的调用规则

规则1:函数模板和普通函数都可以调用,优先调用普通函数

规则2:我们可以加上空的模板参数列表强制调用函数模板

规则3:函数模板可以发生重载


        

为了代码重用,代码就必须是通用的;通用的代码就必须不受数据类型的限制。那么我们可以把数据类型改为一个设计参数。这种类型的程序设计称为参数化程序设计。

1、模板的概念:

        C++除有一种泛型编程的思想,泛型编程的具体例子就是模板。所谓模板,就是一个通用的类型,我们可以向模板传递不同的数据类型,从而得到不同的具体类型。C++中提供的模板有函数模板和类模板;

        函数模板的作用就是将函数的数据类型参数化,比如说我们平常写的函数有具体的形参类型,返回类型。类型参数化可以在我们写函数时将这些数据类型变成未知的变量,从而得到一个函数模板,我们可以根据传入不同的数据类型得到不同的结果和实现。

函数模板的语法:

 template<模板参数表>
 返回类型 函数名(形式参数表)
 { 
    ...;//函数体
 }

<模板参数表>尖括号中不能为空,参数可以有多个,用逗号分开。例如:

 // 一个接受单个类型参数的函数模板  
 template<typename T>
 void print(T value) {
     std::cout << value << std::endl;
 }
 ​
 // 一个接受两个类型参数的函数模板  
 template<typename T1, typename T2>
 void funa(T1 a, T2 b) {}
 ​
 // 一个接受一个类型参数和一个非类型参数的函数模板  
 template<typename T, int N>
 void printArray(const T* array, int size) {
     for (int i = 0; i < (size < N ? size : N); ++i) {
         std::cout << array[i] << " ";
     }
     std::cout << std::endl;
 }

        模板参数主要是模板类型参数。 模板类型参数代表一种类型 ,由关键字class 或 typename (建议用typename) 后加一个标识符构成,在这里两个关键字的意义相同,它们表示后面的参数名代表一个潜在的内置或用户设计的类型。

        template就是语法关键字,表示要声明一个模板;typename语法关键字表示后面紧跟一个虚拟数据类型;

我们可以写一个模板函数 :

 template<class T>
 T my_max(T a, T b)
 {
     return a > b? a:b;
 }

        这个函数的两个形参是一个通用数据类型T,我们可以根据传进去的数据不同得到不同的实现。

        我们如果将T定义为char类型,那么这个函数就变成比较两个char的大小;如果我们将T定义为int类型,那么这个函数的实现就变成比较两个int的大小。

2、使用方法

或者说怎么利用这个模板得到一个具体的函数呢?

下面有两种具体定义一个函数的方法:

2.1让编译器自动类型推导:
 int main()
 {
  my_max(12, 23);
  my_max('a', 'b');
  my_max(12.23, 34.45);
  return 0;
 }

        上面表示让编译器根据我们传进去的数据自己推导T应该成为什么数据类型。

        函数模板根据一组实际类型或(和)值构造出独立的函数的过程通常是隐式发生的,称为模板实参推演。

2.2显式指定数据类型:
 int main()
 {
  my_max<int>(12, 23);
  my_max<char>('a', 'b');
  my_max<double>(12.23, 34.45);
  return 0;
 }

        上面表示显式指定数据类型,我们可以在实参列表前加上声明指定T的数据类型。

3、函数模板的注意事项

3.1函数模板中的通用数据类型要唯一

        即我们不能模板传递不同的数据类型,这样编译器会不知道选择哪一种数据类型。

 template<typename T>
 void func(T& a, T& b)
 {}
 int main()
 {
     func(12,12.23);
     return 0;
 }
3.2函数模板的隐式转换问题

        在函数模板中,如果两个不同类型的实参被传递给接受相同类型参数的模板函数,编译器可能会尝试通过隐式类型转换来匹配模板参数。这可能会导致意外的行为或性能下降。例如:

 template<typename T>
 void func(const T& a,const T& b)
 {}
 int main()
 {
     func<int>(12, 12.23);
     return 0;
 }
 //上述的例子因为使用的不是显式数据指定的方式创建函数模板,那么编译器会不知道T的具体数据类型,所以不会发生隐式的数据转换
 //如果我们使用的是显式地函数模板,那么编译器也可能会自动帮我们进行隐式数据转换。

总结:

  • 如果显式指定了模板参数类型,那么传递给函数的实参类型必须与指定的模板参数类型兼容。

  • 如果让编译器自动推导模板参数类型,那么所有实参的类型必须能够匹配到一个单一的模板参数类型上。

3.3函数模板和普通函数的调用规则
规则1:函数模板和普通函数都可以调用,优先调用普通函数

        当函数模板和普通函数(非模板函数)都可以匹配一个函数调用时,编译器会优先调用普通函数。这是因为普通函数提供了更具体的类型信息,而函数模板则是更一般的解决方案。编译器总是倾向于使用更具体、更明确的函数定义。

规则2:我们可以加上空的模板参数列表强制调用函数模板

        如果希望强制调用函数模板,即使存在可以匹配的普通函数,可以在函数调用时使用空的模板参数列表(<>)。这样做会告诉编译器希望使用模板版本,而不是任何可能存在的普通函数。

 template<typename T>  
 void func(T x) {  
     // 模板函数实现  
 }  
   
 void func(int x) {  
     // 普通函数实现  
 }  
 int main() {  
     func<>(42); // 强制调用模板函数  
     return 0;  
 }

在这个例子中,func<>(42) 会调用模板函数,而不是普通函数。

规则3:函数模板可以发生重载

函数模板可以像普通函数一样被重载。这意味着可以有多个具有相同名称但模板参数不同的函数模板。编译器会根据提供的实参类型来选择最合适的模板进行实例化。

 template<typename T>  
 void func(T x) {  
     // 第一个模板函数实现  
 }  
 template<typename T, typename U>  
 void func(T x, U y) {  
     // 第二个模板函数实现  
 }

在这个例子中,func 被重载了两次,一次接受一个参数,另一次接受两个参数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值