C++函数模板详解

C++函数模板

在我们对不同数据类型的程序逻辑和操作相同时,使用函数模板会非常的方便,只需要定义单个函数模板。函数模板实际上就是对数据类型进行了参数化。
模板的定义都以template开头,后面跟着尖括号括起来的类型参数,类型参数的参数名可以是typename、也可以是class,他们作用是一致的。

#include <iostream>
using namespace std;

template<typename T>
bool compare(T a, T b)
{
	return a > b;
}

int main()
{
	compare<int>(10, 20);
	compare(10.0, 10.2);
	compare("aaa", "bbb");

	return 0;
}

模板函数调用及实参推演

函数模板调用在函数名后面跟上尖括号,尖括号里传入指定的数据类型,和函数实参,如compare<int>(10, 20);
但是不传入类型实参也可以实现模板函数的调用,如compare(10.0, 10.2);,这是因为函数模板可以进行实参推演,可以根据用户传入实参的类型,推导出实参的类型参数的具体类型。
但这样的代码:compare(10, 10.2);编译是不能通过的。因为函数模板中形参的类型是相同的,但传入的实参类型不同,编译器不知道要用哪个类型来实例化,我们可以通过指定具体类型的模板函数来解决这个问题,如:compare<int>(10, 10.2);compare<double>(10, 10.2);

未指定模板类型时,编译器会优先匹配普通函数,没有匹配的普通函数再去类型推导实例化模板函数。

函数模板实例化

函数模板的实例化是在编译阶段完成的,在函数调用点,编译器会从用户指定的类型,从函数模板实例化一份模板函数代码出来,相同的类型参数只会实例化出一份代码,否则会出现函数重定义。
如上面的代码中,compare<int>(10, 20);实例化的模板函数代码如下:


bool compare<int>(int a, int b)
{
	return a > b;
}

函数模板中花括号中的代码,是不参与编译的。因为传入的类型参数可能是类,也可能是普通的数据类型,比如在 编译a > b;时,因为a和b的类型不确定,编译器不知道要进行的是普通的比较,还是执行对象的operator>,所有函数模板的函数体是不参与编译的。

函数模板特例化

在对compare("aaa", "bbb");实例化模板函数时,实例化出这样的代码:

bool compare<const char*>(const char* a, const char* b)
{
	return a > b;
}

在对const char* 进行 > 比较是没有太大意义的,我们实际想要的是 strcmp(a, b) > 0;,这时模板函数的逻辑和操作已经不能满足我们所有的需求了。
对于某些特定类型来说,以来函数模板默认实例化的模板函数代码,不能满足它的处理逻辑时,我们就需要定义模板的特例化
例如对上面compare函数定义的const char*类型的模板特例化:

template<>
bool compare(const char* a, const char* b)
{
	return strcmp(a, b) > 0;
}

在模板特例化时,应定义为template<>,而不是template<typename T>,否则在模板类型推导时,就会实例化出默认的模板函数。函数模板推导是根据形参列表来推导的,所以如果模板特例化时定义为template<typename T>,而形参列表中没有类型T,就不会进行类型推导了。
在这里插入图片描述

跨文件使用模板的坑

在头文件声明模板,在另一个文件定义,再在另一个文件包含头文件使用时,会发生链接错误。
因为函数模板实例化是在编译阶段完成的,而.cpp文件是单独编译,在定义模板的文件中并没有发生函数调用,也就不会实例化出模板函数,链接时就会发生错误。
所以模板的声明和定义要都包含在头文件中。 预编译时就会把模板包含到文件中。
compare.h

template<typename T>
bool compare(T a, T b);

compare.cpp

#include "compare.h"

template<typename T>
bool compare(T a, T b)
{
	return a > b;
}

main.cpp

#include "compare.h"

int main()
{
	compare(10, 20);
}

在这里插入图片描述

函数模板的非类型参数

函数的非类型参数都是常量,只能使用,不能修改。如:

#include <iostream>
using namespace std;

template<typename T, int SIZE>
void sort(T* arr)
{
	for (int i = 0; i < SIZE - 1; i++)
	{
		for (int j = 0; j < SIZE - i - 1; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				T tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

int main()
{
	int arr[] = { 4,5,1,3,6,7,2,8,9 };
	const int size = sizeof(arr) / sizeof(arr[0]);
	sort<int, size>(arr);

	for (int i = 0; i < size ; i++)
	{
		cout << arr[i] << " ";
	}
}

调用模板函数传参时必须要穿常量,否则编译不通过。

C++ 中,**函数模板(Function Template)** 是一种编写**通用函数代码**的机制,它允许我们编写一个函数,可以适用于多种数据类型,而无需为每个类型单独编写函数。这是 C++ 泛型编程的核心机制之一。 --- ## ✅ 函数模板的定义 函数模板使用 `template` 关键字定义,后面跟一个或多个类型参数(通常用 `typename` 或 `class` 声明)。 ### 示例:一个简单的交换函数模板 ```cpp template <typename T> void swapValues(T& a, T& b) { T temp = a; a = b; b = temp; } ``` ### 使用示例: ```cpp int main() { int x = 5, y = 10; swapValues(x, y); std::cout << "x = " << x << ", y = " << y << std::endl; double a = 3.14, b = 2.71; swapValues(a, b); std::cout << "a = " << a << ", b = " << b << std::endl; return 0; } ``` --- ## ✅ 函数模板的实例化 当你调用一个模板函数时,编译器会根据你传入的参数类型自动推导模板参数并生成对应的函数,这个过程叫做 **模板实例化(Template Instantiation)**。 ### 显式实例化(Explicit Instantiation) 你可以显式指定模板参数类型: ```cpp swapValues<int>(x, y); // 显式告诉编译器使用 int 类型 ``` ### 自动推导(Deduction) 大多数情况下,编译器会自动推导: ```cpp swapValues(x, y); // 编译器自动推导为 swapValues<int> ``` --- ## ✅ 函数模板的重载 你可以为特定类型提供不同的实现(即函数模板的特化或重载)。 ### 示例:为 `const char*` 提供特化版本 ```cpp // 通用模板 template <typename T> void printValue(const T& value) { std::cout << value << std::endl; } // 特化版本:处理 const char* template <> void printValue<const char*>(const char* const& value) { std::cout << "String: " << value << std::endl; } ``` ### 使用示例: ```cpp printValue(42); // 输出:42 printValue("Hello"); // 输出:String: Hello ``` --- ## ✅ 函数模板的参数推导与默认参数 ### 参数推导 函数模板可以自动推导参数类型: ```cpp template <typename T> T add(T a, T b) { return a + b; } int result = add(3, 5); // 推导为 add<int> double d = add(2.5, 3.1); // 推导为 add<double> ``` ### 默认模板参数(C++11 及以后) ```cpp template <typename T = int> T multiply(T a, T b) { return a * b; } std::cout << multiply(3, 4) << std::endl; // 使用 int std::cout << multiply<double>(2.5, 3.0) << std::endl; // 使用 double ``` --- ## ✅ 函数模板和可变参数模板(Variadic Templates) C++11 引入了**可变参数模板**,可以定义接受任意数量参数的模板函数。 ### 示例:打印任意数量的参数 ```cpp #include <iostream> template <typename T> void print(T value) { std::cout << value << std::endl; } template <typename T, typename... Args> void print(T first, Args... args) { std::cout << first << " "; print(args...); } int main() { print(1, 2, 3, "Hello", 4.5); return 0; } ``` --- ## ✅ 函数模板的限制与 SFINAE 有时我们需要限制模板只适用于某些类型。可以使用 **SFINAE(Substitution Failure Is Not An Error)** 技术。 ### 示例:只允许算术类型 ```cpp #include <type_traits> template <typename T> typename std::enable_if<std::is_arithmetic<T>::value, T>::type add(T a, T b) { return a + b; } ``` --- ## ✅ 总结 | 概念 | 说明 | |------|------| | 函数模板 | 使用 `template<typename T>` 定义通用函数 | | 实例化 | 编译器根据参数自动推导或显式指定类型 | | 重载/特化 | 可以为特定类型提供不同的实现 | | 可变参数模板 | 使用 `typename... Args` 实现任意参数 | | SFINAE | 用于限制模板只适用于某些类型 | | 默认模板参数 | C++11 支持为模板参数设置默认值 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_200_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值