C++变参模板

        从c++11开始,模板可以接受一组数量可变的参数,这种技术称为变参模板。

变参模板

        下面一个例子,通过变参模板打印一组数量和类型都不确定的参数。

#include <iostream>
#include <string>void print(void)
{std::cout<<"........................"<<std::endl;
}template <typename T, typename ... Ts>
void print(T arg1, Ts ... args)
{std::cout<<arg1<<std::endl;print(args ...);
}int main(int argc, char **argv)
{print("hello", 7.5, 10, std::string("building"));
}

        看到上面这段代码,首先会产生两个疑问:

  • void print(T arg1, Ts ... args)中arg1是什么作用?
  • void print(void)有什么作用?

        下面将通过解释这段代码的运行过程,来解答上面的问题。仔细观察上面这段代码,不难发现,print函数模板是一个递归函数模板。执行过程大体如下:

  1. main函数调用print("hello", 7.5, 10, std::string("building"));,"hello"赋值给arg1,其余参数赋值给args
  2. print函数输出"hello",再次调用自身print(7.5, 10, std::string("building"));,7.5赋值给arg1,其余参数赋值给args
  3. print函数输出7.5,再次调用自身print(10, std::string("building"));,10赋值给arg1,其余参数赋值给args
  4. print函数输出10,再次调用自身print(std::string("building"));,"building"赋值给arg1,args为空
  5. print函数输出"building",因为args为空,此时不在调用自身,而是重载函数print(void),然后结束递归。

        从整个过程来看,arg1的主要作用就是从args迭代取值,print(void)负责处理args为空的情况。那么不定义void print(void)是否可以呢?答案是否定的,不定义该函数,编译将会报错“No matching function for call to 'print'”。

        此处还应该注意一个问题,print和c/c++的printf原理不一样:printf通过va_list实现变参,而print函数模板是为每种情况都生成了一个重载函数,如下:

        上面的信息来自于xcode调试,当然,也可以通过objdump查看,也会得到相同的结果,编译器确实生成了多个print重载函数:

         当然,上面的代码还可以写成下面的样子:

template <typename T>
void print(T arg)
{std::cout<<arg<<std::endl;
}template <typename T, typename ... Ts>
void print(T arg1, Ts ... args)
{print(arg1);print(args ...);
}

        如果代码中没有print(arg1),程序知会打印最后一个参数building,print只有迭代到最后一个参数时,才会找到合适的函数print(T arg)。

        但一定要注意,下面的实现方式是错误的,无递归结束条件,无限迭代,直到耗尽堆栈空间:

void print(void)
{
}template <typename ... Ts>
void print(Ts ... args)
{print(args ...);
}

折叠表达式

        从c++17开始,c++引入了一种更为简洁灵活的编程方式——折叠表达式,下面是一个简单的例子:

#include <cstdio>template <typename ...T>
auto sum(T ... args)
{return (... + args);
}int main(int argc, char **argv)
{int s = sum(1, 2, 3, 4, 5);printf("%d\n", s);
}

        几乎所有的二元运算符都可以用于折叠表达式,下面是一些其他运算符的例子:

template <typename F, typename ...T>
auto apply(F f, T ...args)
{return (f(args), ...);
}template <typename ...T>
bool and_op(T ...args)
{return (args && ...);
}

        迭代表达式,仅仅是围绕一个操作符简单地展开,例如连加。因此,对于三元操作符:?,很难用迭代表达式来实现。所以,想使用迭代表达式和:?求一个集合中的极值,是无法实现的。但是可以通过其他方式实现,下面便是一种实现方式:

template <typename T>
struct min_op final
{
public:min_op(T data) : is_first(true), min_data(data) {}T operator()(T rhs) {if (is_first) {is_first = false;min_data = rhs;return min_data;}min_data = min_data < rhs ? min_data : rhs;return min_data;}private:bool is_first;T min_data;};template <typename T, typename ...Ts>
auto min(T arg, Ts ... args)
{min_op<T> op(arg);return (op(args), ...);
}

        很明显,这种方式还不如直接使用for循环直接利索。

        与之前的递归迭代方式相比,迭代表达式最大的优点是编译器没有为其生成过多的重载函数。迭代表达式与之前的优点:不使用vector,不会生成多个函数,缺点:解决元素较少的情况。

        变参模板的优点:

  • 可以支持不同的类型
  • 可以不使用容器
  • 直接访问元素,效率比较高

        但其并不是完美无缺的,:

  • 不适用元素较多的情况
  • 使用递归迭代会生成大量的重载函数

变参类模板和变参表达式

变参表达式

        函数参数包除了转发所有参数外,还可以做其他事,例如计算他们的值。

template <typename ... Ts>
void print_doubled(Ts ... args)
{print((args + args) ...);
}...
print_doubled(1, 2, 3, 4, 5, 6);
...

变参下标

        作为另外一个例子,下面的函数通过一组变参下标来访问第一个参数中相应的元素:

template<typename T, typename ...IDS>
void print_elems(T a, IDS ...ids)
{print(a[ids]...);
}...
std::vector<int> v{1, 2, 3, 4, 5, 6};
print_elems(v, 1, 3, 5);
...

变参模板类

        提到变参模板类,首先会想到std::tuple,该种技术使得不定义新类型的前提下,多值返回成为一种可能,提供了更加灵活的编程方式,例如:

template <typename T>
std::tuple<T, T, T,  T> calc(T x, T y)
{return std::make_tuple(x + y, x - y, x * y, x / y);
}...
auto result = calc(10.0, 2.5);
...

 变参基类

        变参基类从不定数的基类派生出一个新的类,主要目的是代码复用,比普通写法更加方便,派生类无需引入基类头文件,但需要注意多继承陷阱。下面是一个简单的例子:

#include <cstdio>struct fly_animal
{void fly(void) { printf("flying !\n"); }
};struct swim_animal
{void swim(void) { printf("swiming !\n"); }
};struct run_animal
{void run(void) { printf("running !\n"); }
};struct fish 
{//...
};struct bird 
{//...
};struct mammal
{//....
};template <typename ...Bases>
struct overloader : Bases...
{//using Bases::operator()...;
};int main(int argc, const char **argv)
{using flyfish = overloader<fly_animal, swim_animal>;flyfish ff;ff.fly();ff.swim();using crocodile = overloader<run_animal, swim_animal>;crocodile ccdl;ccdl.run();ccdl.swim();using cat = overloader<mammal, run_animal>;cat ct;ct.run();return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/273283.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络 —— 运输层

运输层 5.1 运输层概述 运输层的主要任务是&#xff0c;如何为运行在不同主机上的应用进程提供直接的通信服务。运输层协议又称为端到端协议。 根据应用需求的不同&#xff0c;因特网的运输层为应用层提供了两种不同的运输协议&#xff0c;即面向连接的TCP和无连接的UDP 5.2…

Chrome中如何导出和导入书签

导出书签 如下图所示&#xff1a; 右上角三点->书签和清单->书签管理器->右上角三点->导出书签 然后你选择保存地址即可。打开后如下&#xff1a; 导入书签 如下图所示&#xff1a; 右上角三点->书签和清单->导入书签和设置->选择以前导出的书签&…

0103n阶行列式-行列式-线性代数

文章目录 一 n阶行列式二 三阶行列式三 特殊行列式结语 一 n阶行列式 ∣ a 11 a 12 ⋯ a 1 n a 21 a 22 ⋯ a 2 n ⋯ ⋯ ⋯ ⋯ a n 1 a n 2 ⋯ a n n ∣ \begin{vmatrix}a_{11}&a_{12}&\cdots&a_{1n}\\a_{21}&a_{22}&\cdots&a_{2n}\\\cdots&\cdots…

【大厂AI课学习笔记NO.68】开源和开源发展情况

开源即源代码公开&#xff0c;任何人能获取源代码&#xff0c;查看、修改、分发他们认为合适的代码。 依托同行评审和社区生成&#xff0c;旨在以分散、协作的方式开发。 我们曾经很详细的讨论过开源协议的问题&#xff0c;详细可以参考我的文章&#xff1a; https://giszz.…

政安晨:【深度学习处理实践】(五)—— 初识RNN-循环神经网络

RNN&#xff08;循环神经网络&#xff09;是一种在深度学习中常用的神经网络结构&#xff0c;用于处理序列数据。与传统的前馈神经网络不同&#xff0c;RNN通过引入循环连接在网络中保留了历史信息。 RNN中的每个神经元都有一个隐藏状态&#xff0c;它会根据当前输入和前一个时…

Linux(Ubuntu)中安装vscode

①首先去vscode的官网下载.deb文件 网址&#xff1a;https://code.visualstudio.com/docs/?dvlinuxarm64_deb 注&#xff1a;如果linux端无法打开网页下载文件&#xff0c;可以在Windows端下载好用WinSCP传输到Linux。下载前注意下你的系统架构是arm还是amd&#xff0c;系统…

Linux:kubernetes(k8s)lable和selecto标签和选择器的使用(11)

通过标签是可以让我们的容器和容器之间相互认识&#xff0c;简单来说一边打了标签&#xff0c;一边使用选择器去选择就可以快速的让他们之间耦合 定义标签有两种办法&#xff0c;一个是文件中&#xff0c;一个是命令行里 我们在前几章编进文件的时候里面都有lable比如 这个就是…

搜维尔科技:动作捕捉与数字时尚:Wondar Studios欧莱雅项目

来自意大利的Wondar Studios工作室&#xff0c;是一家制作与动作捕捉技术相关软件和内容的公司&#xff0c;其出品的三维角色动画均由专业动捕系统真实录制制作。 我们很高兴与大家分享Wondar Studios最新的动捕项目&#xff0c;该项目带来了身临其境的虚拟现实体验。他们与巴…

VUE_自适应布局lib-flexible+postcss-pxtorem、lib-flexible + postcss-px2rem,nuxt页面自适配

lib-flexible postcss-pxtorem适配 我采用的是flexable.js和postcss-pxtorem。我一开始用的是postcss-px2rem后来发现和nuxt引入公共css的时候发生了冲突所以改用了postcss-pxtorem。 安装依赖 npm i lib-flexible -S npm install postcss-pxtorem --save 1、lib-flexible.…

Windows10/11配置WSL(Ubuntu)环境

文章目录 WSL介绍WSL部署扩展&#xff1a;辅助工具Windosw Terminal安装下载 WSL介绍 传统方式获取Linux操作系统&#xff0c;是安装完整的虚拟机及镜像环境&#xff0c;例如虚拟机VMware 而使用WSL,可以以非常轻量化的方式&#xff0c;得到Linux系统环境 它无需单独虚拟一套硬…

excel统计分析——抽样

参考资料&#xff1a;生物统计学 在科学研究和生产实践中&#xff0c;需要对所研究的总体进行全面了解&#xff0c;但由于人力、物力和时间的限制&#xff0c;不可能对总体的每个个体都进行观测&#xff0c;而只能抽取其中的一部分个体加以研究&#xff0c;并由样本的结果对总体…

UE4 Niagara 关卡4.1官方案例解析

we now directly supporting playing audio from arbitrary locations in particle systems.users have control over volume and pitch,and the system can directly play sound waves,or sound cues which have multiple waves in them.(我们现在直接支持在粒子系统中从任意…

OB_GINS学习

OB_GINS学习 组合导航中的杆臂测量加速度计的零偏单位转换受到经纬度以及高程影响的正常重力位的计算公式大地坐标系&#xff08;LBH&#xff09;向空间直角坐标系&#xff08;XYZ&#xff09;的转换及其逆转换导航坐标系&#xff08;n系&#xff09;到地心地固坐标系&#xff…

第二十二周周报

论文研读&#xff1a;Camera Distance-aware Top-down Approach for 3D Multi-person Pose Estimation from a Single RGB Image 粗读10篇文献。 通过图2 我可以知道这个论文大概实现的这个姿态估计效果的方法&#xff0c;首先是把图片输入到DetectNet网络&#xff0c;该网络…

HTML概念

文章目录 1. HTML 概念1.1. 简介1.2. 思想1.3. 特点1.4. 语法1.4.1. 标签1.4.2. 属性1.4.3. 标签体1.4.4. 注释 2. HTML 实体2.1. 练习 3. HTML 结构3.1. <!DOCTYPE html>声明3.2. html根标签 4. 补充4.1. 管理文件4.2. 配置 VsCode4.2. 配置 VsCode 1. HTML 概念 1.1. 简…

PyQt6实战1

创建一个json处理的小工具 功能&#xff1a; 1.json格式化 2.jsonpath提取数据 3.保存文件 main.py from PyQt6.QtGui import QFocusEvent from PyQt6.QtWidgets import * from PyQt6.QtCore import * from PyQt6.QtGui import * import sys import json import time impo…

有什么针对新闻媒体行业的安全解决方案

对媒体行业而言&#xff0c;门户网站是最易受到攻击的地方。常见的攻击方式有网页篡改、挂马和被植入暗链等。门户网站作为新闻媒体对外的第一扇门&#xff0c;通常承载了大量的流量&#xff0c;一旦遭到攻击&#xff0c;造成的影响会更具有可怕的“传播力”。那么我们应该如何…

【逆向实战 某视频防盗链参数的生成】防盗链cKey的生成,还要补环境?还是单嵌套的webpack?

逆向日期&#xff1a;2024.03.10 使用工具&#xff1a;Node.js 类型&#xff1a;单嵌套Webpack 文章全程已做去敏处理&#xff01;&#xff01;&#xff01; 【需要做的可联系我】 AES解密处理&#xff08;直接解密即可&#xff09;&#xff08;crypto-js.js 标准算法&#xf…

我们的一生都是在挤火车。

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 昨天从燕郊坐火车回石家庄&#xff0c;由于赶上元旦假期&#xff0c;所有高铁票都售罄&#xff0c;一张普通火车票&#xff0c;还是一周前就买才买到的。 从燕郊站&#xff0c;到北京站&#xff0c;然后地铁去北京西站…

JWT的是什么

session共享 什么是session共享 Session共享是指在分布式系统中&#xff0c;在多个服务器之间共享同一个用户的会话数据。在传统的Web应用中&#xff0c;用户的会话信息通常存储在服务器端的Session中&#xff0c;而每个用户的请求在同一个服务器上处理&#xff0c;因此可以轻…