当前位置: 首页 > article >正文

C++模板编程——可变参函数模板

目录

1. 可变参函数模板基本介绍

2. 参数包展开——通过递归函数

3. 参数包展开——通过编译期间if语句(constexpr if)

4. 重载

5. 后记


进来看的小伙伴们应该对C++中的模板有了一定了解,下面给大家介绍一下可变参函数模板。过于基础的概念将不仔细介绍。

1. 可变参函数模板基本介绍

先来看一个可变参函数模板的示例:

#include <iostream>

using namespace std;

template<typename... T>
void myFunc(T... args)
{
	cout << "----------------MyFunc begin -----------------" << endl;

	cout << "收到的参数个数:" << sizeof...(args) << endl;
	cout << "收到的参数类型个数:" << sizeof...(T) << endl;

	cout << "----------------MyFunc end   -----------------" << endl;
}


int main()
{
	myFunc();
	myFunc(1);
	myFunc(1, 2);
	myFunc(1, 2, 3);
}

运行结果如下图:

仔细观察上面给出的代码,有三点需要注意:

  1. template行中typename后面跟着三个点,然后是类型模板参数T
  2. 在函数签名行void myFunc(T... args),因为T后面跟着三个点,所以T称为可变参类型。看起来是一个类型,实际上其代表了0个到多个的类型。 args称为一包参数,每个参数的类型都可以为任意类型。
  3. 在C++11引入了sizeof...()的语法,这是固定写法,用于可变参函数模板或可变参模板内部,表示模板参数个数或类型数量。只有这种...的可变参才可以使用。

上面的代码有一个问题,在拿到了一堆参数args之后,我们需要拿到每一个参数进行处理。这就涉及到了参数包的展开,下面将介绍两种参数包展开的两种方法。

2. 参数包展开——通过递归函数

参数包展开的套路是比较固定的,通过递归函数进行参数包展开是一种经典手法。

为了实现递归函数的方式展开参数包,需要编写一个参数包展开函数和一个同名的递归终止函数

这两个函数一起实现参数包的展开。

先看代码:

#include <iostream>

using namespace std;

void myFunc() // 同名的参数包递归终止函数
{
	cout << "参数包展开的递归终止函数" << endl;
}

template<typename T, typename... U>
void myFunc(T arg1, U... args) // 参数包展开函数
{
	cout << "收到的参数为:" << arg1 << endl;
	myFunc(args...);
}

int main()
{
	myFunc(1, 2, 3);
}

运行截图如下图所示:

通过代码可以看到参数包展开函数是一个函数模板。void myFunc(T arg1, U... args);它的函数签名中,T为1个参数,U为0个或多个参数类型。所以这个函数模板最少需要一个参数

通过代码可以看到,递归终止函数为一个同名的普通的函数(不是函数模板)接收0个参数


在main函数中调用myFunc(1,2,3);时,1会匹配到arg1,2和3会匹配到args。

在myFunc内部会调用myFunc(2,3);,2会匹配到arg1,3会匹配到args。

myFunc内部继续调用myFunc(3);,3会匹配到arg1,args没有匹配到任何参数。

接着会调用myFunc(); 调用递归终止函数。

通过这种递归调用的方式就实现了参数包的展开,这是一种固定的编程手法,理解学会就好了。

3. 参数包展开——通过编译期间if语句(constexpr if)

在C++17标准中增加了编译期间if语句。与常规的if语句类似,但是在if语句后增加了一个constexpr关建字,这个关建字代表编译时求值。

有了编译期间if语句就非常方便了,不需要编写递归终止函数了。具体看下面的代码:

#include <iostream>

using namespace std;

template<typename T, typename ...U>
void myFunc(T arg1, U... args)
{
	cout << "收到的参数为:" << arg1 << endl;
	if constexpr (sizeof...(args) > 0)
	{
		myFunc(args...);
	}
	else
	{
		cout << "没有更多参数了" << endl;
	}
}

int main()
{
	myFunc(1, 2, 3);
}

运行结果如下图所示:

使用if constexpr和sizeof...()来判断args中含有的参数个数。当剩余的参数个数大于0时,继续递归调用,否则会执行else分支。

非常好理解,而且编写起来也非常方便。

需要注意的是,if constexpr所指定的条件必须在编译期间能够求值,不可以是变量。

4. 重载

可变参函数模板也可以重载,具体见下面的代码。

#include <iostream>

using namespace std;

template<typename... T>
void myFunc(T... args)
{
	cout << "void myFunc(T... args); 执行了" << endl;
}

template<typename... T>
void myFunc(T*... args)
{
	cout << "void myFunc(T*... args); 执行了" << endl;
}

void myFunc(int arg)
{
	cout << "void myFunc(int arg); 执行了" << endl;
}


int main()
{
	myFunc(1, 2, 3);
	myFunc((int*)nullptr);
	myFunc(1);
}

运行截图如下所示:

5. 后记

可变参函数模板还是比较好理解的,有讲得不对的地方敬请批评指正。


http://www.kler.cn/a/529358.html

相关文章:

  • 全覆盖路径规划-精准细胞覆盖算法
  • Shell特殊状态变量以及常用内置变量总结
  • 基于微信小程序的酒店管理系统设计与实现(源码+数据库+文档)
  • Cocos Creator 3.8 2D 游戏开发知识点整理
  • Spring JDBC:简化数据库操作的利器
  • Redis 基础命令
  • 无法将“mklink”项识别为 cmdlet、函数、脚本文件或可运行程序的名称
  • MySQL知识点总结(十九)
  • Excel to form ?一键导入微软表单
  • three.js+WebGL踩坑经验合集(6.2):负缩放,负定矩阵和行列式的关系(3D版本)
  • 一文讲解Java中HashMap的扩容机制
  • 解锁计算机视觉算法:从理论到代码实战
  • 小白零基础--CPP多线程
  • w186格障碍诊断系统spring boot设计与实现
  • 星际智慧农业系统(SAS),智慧农业的未来篇章
  • MP4分析工具
  • 97,【5】buuctf web [极客大挑战 2020]Greatphp
  • python算法和数据结构刷题[5]:动态规划
  • 【Springboot2】热部署开启
  • 【人工智能】 在本地运行 DeepSeek 模型:Ollama 安装指南
  • deep generative model stanford lecture note2 --- autoregressive
  • Windows11 不依赖docker搭建 deepseek-R1 1.5B版本(附 Open WebUi搭建方式)
  • openmv运行时突然中断并且没断联只是跟复位了一样
  • 如何在Intellij IDEA中识别一个文件夹下的多个Maven module?
  • 【单层神经网络】基于MXNet库简化实现线性回归
  • Python sider-ai-api库 — 访问Claude、llama、ChatGPT、gemini、o1等大模型API