tuple基本用法

元组简介

C++11 标准新引入了一种类模板,命名为 tuple(中文可直译为元组)。tuple 最大的特点是:实例化的对象可以存储任意数量、任意类型的数据。

tuple 的应用场景很广泛,例如当需要存储多个不同类型的元素时,可以使用 tuple;当函数需要返回多个数据时,可以将这些数据存储在 tuple 中,函数只需返回一个 tuple 对象即可。

tuple对象的创建

tuple 本质是一个以可变模板参数定义的类模板,它定义在 头文件并位于 std 命名空间中。因此要想使用 tuple 类模板,程序中需要首先引入以下代码:

#include <tuple>
using std::tuple;

实例化 tuple 模板类对象常用的方法有两种,一种是借助该类的构造函数,另一种是借助 make_tuple() 函数。

类的构造函数

1) 默认构造函数
   constexpr tuple();

2) 拷贝构造函数
   tuple (const tuple& tpl);

3) 移动构造函数
   tuple (tuple&& tpl);

4) 隐式类型转换构造函数
   template <class... UTypes>
       tuple (const tuple<UTypes...>& tpl); //左值方式
   template <class... UTypes>
       tuple (tuple<UTypes...>&& tpl);      //右值方式

5) 支持初始化列表的构造函数
   explicit tuple (const Types&... elems);  //左值方式
   template <class... UTypes>
       explicit tuple (UTypes&&... elems);  //右值方式

6) 将pair对象转换为tuple对象
   template <class U1, class U2>
       tuple (const pair<U1,U2>& pr);       //左值方式
   template <class U1, class U2>
       tuple (pair<U1,U2>&& pr);            //右值方式

make_tuple()函数
上面程序中,我们已经用到了 make_tuple() 函数,它以模板的形式定义在 <tuple> 头文件中,功能是创建一个 tuple 右值对象(或者临时对象)。

对于 make_tuple() 函数创建了 tuple 对象,我们可以上面程序中那样作为移动构造函数的参数,也可以这样用:

auto first = std::make_tuple (10,'a');   // tuple < int, char >
const int a = 0; int b[3];
auto second = std::make_tuple (a,b);     // tuple < int, int* >

程序中分别创建了 first 和 second 两个 tuple 对象,它们的类型可以直接用 auto 表示。

创建一个空的元组, 创建时,需要指定元组的数据类型

std::tuple<int, float, double, long, long long> first;

创建一个元组并初始化元组

std::string str_second_1("_1");
std::string str_second_2("_2");

// 指定了元素类型为引用 和 std::string, 下面两种方式都是可以的,只不过第二个参数不同而已
std::tuple<std::string, std::string> second_1(str_second_1, std::string("_2"));
std::tuple<std::string, std::string> second_2(str_second_1, str_second_2);

创建一个元素是引用的元组

//3、创建一个元组,元组的元素可以被引用, 这里以 int 为例
int i_third = 3;
std::tuple<int&> third(std::ref(i_third));

使用make_tuple创建元组

int i_fourth_1 = 4;
int i_fourth_2 = 44;
// 下面的两种方式都可以
std::tuple<int, int> forth_1 = std::make_tuple(i_fourth_1, i_fourth_2);
auto forth_2 = std::make_tuple(i_fourth_1, i_fourth_2);

创建一个类型为引用的元组, 对元组的修改。 这里以 std::string为例

std::string str_five_1("five_1");
// 输出原址值
std::cout << "str_five_1 = " << str_five_1.c_str() << "\n";

std::tuple<std::string&, int> five(str_five_1, 5);
// 通过元组 对第一个元素的修改,str_five_1的值也会跟着修改,因为元组的第一个元素类型为引用。
// 使用get访问元组的第一个元素
std::get<0>(five) = "five_2";

// 输出的将是: five_2
std::cout << "str_five_1 = " << str_five_1.c_str() << "\n";

计算元组的元素个数

需要函数: std::tuple_size。 下面是一个例子

std::tuple<char, int, long, std::string> first('A', 2, 3, "4");
// 使用std::tuple_size计算元组个数
int i_count = std::tuple_size<decltype(first)>::value;
std::cout << "元组个数=" << i_count << "\n";  //4个

访问元素

访问元组的元素,需要函数: std::get<index>(obj)。其中:[index]是元组中元素的下标,0,1,2,3,4,… [obj]-元组变量。

std::tuple<char, int, long, std::string> second('A', 2, 3, "4");
int index = 0;
std::cout << index++ << " = " << std::get<0>(second) << "\n";  //0=A
std::cout << index++ << " = " << std::get<1>(second) << "\n";  //1=2
std::cout << index++ << " = " << std::get<2>(second) << "\n";  //2=3
std::cout << index++ << " = " << std::get<3>(second).c_str() << "\n"; //3=4

元组不支持迭代访问,且只能通过索引(或者tie解包:将元组的中每一个元素提取到指定变量中)访问,且索引不能动态传入。上面的代码中,索引都是在编译器编译期间就确定了。下面的演示代码将会在编译期间出错。

for (int i = 0; i < 3; i++)
       std::cout << index++ << " = " << std::get<i>(second) << "\n";  // 无法通过编译

获取元素的类型

获取元组中某个元素的数据类型,需要用到另外一个类型:std::tuple_element 。 语法:std::tuple_element<index, tuple> 。 [index]-元组中元素的索引,[tuple]哪一个元组。

std::tuple<int, std::string> third(9, std::string("ABC"));

// 得到元组第1个元素的类型,用元组第一个元素的类型声明一个变量
std::tuple_element<1, decltype(third)>::type val_1;

// 获取元组的第一个元素的值
val_1 = std::get<1>(third);
std::cout << "val_1 = " << val_1.c_str() << "\n"; //val_1 = ABC

使用 std::tie解包

元组,可以看作一个包,类比结构体。 需要访问元组的元素时,2 种方法: A、索引访问,B、std::tie。

元组包含一个或者多个元素,使用std::tie解包:首先需要定义对应元素的变量,再使用tie。 比如,元素第0个元素的类型时 char,第1个元素类型时int,那么,需要定义一个 char的变量和int的变量,用来储存解包元素的结果。

std::tuple<char, int, long, std::string> fourth('A', 2, 3, "4");
 
// 定义变量,保存解包结果
char tuple_0 = '0';
int tuple_1 = 0;
long tuple_2 = 0;
std::string tuple_3("");
 
// 使用std::tie, 依次传入对应的解包变量
std::tie(tuple_0, tuple_1, tuple_2, tuple_3) = fourth;

// 输出解包结果
std::cout << "tuple_0 = " << tuple_0 << "\n"; //tuple_0 = A
std::cout << "tuple_1 = " << tuple_1 << "\n"; //tuple_1 = 2
std::cout << "tuple_2 = " << tuple_2 << "\n"; //tuple_2 = 3
std::cout << "tuple_3 = " << tuple_3.c_str() << "\n"; //tuple_3 = 4

std::tie的结构定义如下:

template<class... _Types> inline
    constexpr tuple<_Types&...>
        tie(_Types&... _Args) _NOEXCEPT
{    // make tuple from elements
    typedef tuple<_Types&...> _Ttype;
    return (_Ttype(_Args...));
}

接着 std::tie 解包。 如果一个元组,只需要取出其中特定位置上的元素,不用把每一个元素取出来。比如:只要索引为偶数的元素。元组提供了类似占位符的功能:std::ignore。满足上面的需求,只需要在索引为奇数的位置填上std::ignore。一个例子:

std::tuple<char, int, long, std::string> fourth('A', 2, 3, "4");

// 定义变量,保存解包结果
char tuple_0 = '0';
int tuple_1 = 0;
long tuple_2 = 0;
std::string tuple_3("");

// 使用占位符
std::tie(tuple_0, std::ignore, tuple_2, std::ignore) = fourth;

// 输出解包结果
std::cout << "tuple_0 = " << tuple_0 << "\n"; //tuple_0 = A
std::cout << "tuple_1 = " << tuple_1 << "\n"; //tuple_1 = 0
std::cout << "tuple_2 = " << tuple_2 << "\n"; //tuple_2 = 3
std::cout << "tuple_3 = " << tuple_3.c_str() << "\n"; //tuple_3 =

元组连接(拼接)

使用 std::tuple_cat 执行拼接。

std::tuple<char, int, double> first('A', 1, 2.2f);

// 组合到一起, 使用auto, 自动推导
auto second = std::tuple_cat(first, std::make_tuple('B', std::string("-=+")));
// 组合到一起,可以知道每一个元素的数据类型时什么 与 auto推导效果一样
std::tuple<char, int, double, char, std::string> third = std::tuple_cat(first, std::make_tuple('B', std::string("-=+")));

// 输出合并后的元组内容
int index = 0;
std::cout << index++ << " = " << std::get<0>(second) << "\n"; //0 = A
std::cout << index++ << " = " << std::get<1>(second) << "\n"; //1 = 1
std::cout << index++ << " = " << std::get<2>(second) << "\n"; //2 = 2.2

std::cout << index++ << " = " << std::get<3>(second) << "\n"; //3 = B
std::cout << index++ << " = " << std::get<4>(second).c_str() << "\n"; //4 = -=+

std::cout << index++ << " = " << std::get<0>(second) << "\n"; //0 = A
std::cout << index++ << " = " << std::get<1>(second) << "\n"; //1 = 1
std::cout << index++ << " = " << std::get<2>(second) << "\n"; //2 = 2.2

std::cout << index++ << " = " << std::get<3>(second) << "\n"; //3 = B
std::cout << index++ << " = " << std::get<4>(second).c_str() << "\n"; //4 = -=+

遍历

这里将采用的时 递归遍历,需要注意,考虑爆栈的情况。其实,tuple也是基于模板的STL容器。 因为其可以容纳多个参数,且每个参数类型可不相同,遍历输出则涉及到参数展开的情况,这里以递归的方式实现遍历,核心代码:

#include <iostream>
#include <tuple>

template<typename Tuple, size_t N>
struct tuple_show
{
	static void show(const Tuple& t, std::ostream& os)
	{
		tuple_show<Tuple, N - 1>::show(t, os);
		os << ", " << std::get<N - 1>(t);
	}
};


// 偏特性,可以理解为递归的终止
template<typename Tuple>
struct tuple_show < Tuple, 1>
{
	static void show(const Tuple& t, std::ostream& os)
	{
		os << std::get<0>(t);
	}
};

// 自己写个函数,调用上面的递归展开,
template<typename... Args>
std::ostream& operator << (std::ostream& os, const std::tuple<Args...>& t)
{
	os << "[";
	tuple_show<decltype(t), sizeof...(Args)>::show(t, os);
	os << "]";

	return os;
}

int main()
{
	auto t1 = std::make_tuple(1, 'A', "-=+", 2);
	std::cout << t1;

	return 0;
}

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

高压功率放大器在木结构的螺栓连接松动检测系统中的应用

实验名称&#xff1a;功率放大器在面向木结构的螺栓连接松动检测系统中的应用 实验设备&#xff1a; 计算机、压电传感器PZT、D型数显扭矩扳手、NIELVISII&#xff0b;数据采集卡、ATA-2021H功率放大器等。 实验过程&#xff1a; 设计了一种基于压电时间反演法的木材连接螺栓松…

Menards EDI对接流程

Menards是一家美国的家居建材零售商&#xff0c;成立于1962年&#xff0c;总部位于美国威斯康星州的伊甸谷市。该公司经营各种家居建材产品&#xff0c;包括木材、地板、墙纸、厨房卫浴用品等&#xff0c;并拥有超过300家门店&#xff0c;分布在美国中西部和北部地区。Menards的…

Beats:如何在 Docker 容器中运行 Filebeat

今天在这篇博客中&#xff0c;我们将学习如何在容器环境中运行 Filebeat。 为了快速了解 Filebeat 是做什么用的&#xff1a; Filebeat用于转发和集中日志数据它重量轻&#xff0c;小型化&#xff0c;使用的资源更少它作为代理安装在你的服务器上它监视来自指定位置的日志文件…

[CTF/网络安全] 攻防世界 weak_auth 解题详析

[CTF/网络安全] 攻防世界 weak_auth 解题详析 弱认证弱认证绕过方法姿势Burp Suite 爆破 总结 题目描述&#xff1a;小宁写了一个登陆验证页面&#xff0c;随手就设了一个密码。 弱认证 weak_auth翻译&#xff1a;弱认证 这个术语通常用来描述一种较弱的安全认证方法或机制&am…

UVC调用过程部分细节分析

UVC调用过程部分细节分析 文章目录 UVC调用过程部分细节分析概括分析UVC驱动调用过程1.open:ioctl 2.VIDIOC_QUERYCAP3.VIDIOC_ENUM_FMT4.VIDIOC_G_FMT5.VIDIOC_TRY_FMT6.VIDIOC_S_FMT /7.VIDIOC_REQBUFS8.VIDIOC_QUERYBUF9.mmap10.VIDIOC_QBUF11.VIDIOC_STREAMON12.poll13.VID…

[CTF/网络安全] 攻防世界 backup 解题详析

[CTF/网络安全] 攻防世界 backup 解题详析 PHP备份文件名备份文件漏洞成因备份文件名常用后缀姿势总结 题目描述&#xff1a;X老师忘记删除备份文件&#xff0c;他派小宁同学去把备份文件找出来,一起来帮小宁同学吧&#xff01; PHP备份文件名 PHP 脚本文件的备份文件名&#…

MATLAB 之 其他形式的二维图形

文章目录 一、对函数自适应采样的绘图函数二、其他坐标系下的二维曲线图1. 对数坐标函数2. 极坐标图 三、其他特殊二维图像1. 条形类图形2. 面积类图形3. 散点类图形4. 矢量类图形 二维图线除采用直角坐标系外&#xff0c;还可采用对数坐标或极坐标。除了绘制二维曲线外&#x…

Anaconda安装与Python环境搭建

这篇文章介绍了如何安装Anaconda&#xff0c;及Python环境如何配置&#xff0c;你是否还在为难以寻找一篇讲述全面的环境配置博客而苦恼&#xff0c;稍安勿躁&#xff0c;你找对啦&#xff0c;照着本篇文章做下去&#xff0c;你就会发现没那么难呢&#xff01; Anaconda安装 …

Python系列模块之标准库OS详解

感谢点赞和关注 &#xff0c;每天进步一点点&#xff01;加油&#xff01; 目录 ​一、模块 1.1 模块的定义 1.2 模块的分类 1.3 模块的基本导入语法 二、Python中的包 三、标准库之os模块 实战&#xff1a; 钉钉告警应用 一、模块 1.1 模块的定义 Python 模块(Module)&a…

目标检测数据预处理——部件截图,按一定比例进行外扩

本片是截图的篇的升级版本&#xff0c;简单版本的截图请参考根据目标框外扩一定比例进行截图&#xff08;连带标签&#xff09;。 对目标框&#xff08;类别名称&#xff09;进行分类&#xff0c;将同一类的目标框进行截图并分类保存在不同的文件夹中。 在本篇当中&#xff0c;…

Flink有状态计算的状态容错

状态容错 State Fault Tolerance 首先来说一说状态容错。Flink 支持有状态的计算&#xff0c;可以把数据流的结果一直维持在内存&#xff08;或 disk&#xff09;中&#xff0c;比如累加一个点击数&#xff0c;如果某一时刻计算程序挂掉了&#xff0c;如何保证下次重启的时候&…

一文了解customRef 自定义ref使用

概念 按照文档中的说明&#xff1a;customRef 可以用来创建一个自定义的 ref&#xff0c;并对其依赖项跟踪和更新触发进行显式控制。它需要一个工厂函数&#xff0c;该函数接收 track 和trigger函数作为参数&#xff0c;并且应该返回一个带有 get 和 set 的对象。 其实大致意思…

5.2 标准IO:文件的打开、关闭及代码实现

目录 标准IO 文件的打开 标准I/O-fopen-mode参数 ​编辑 标准I/O-fopen-示例 标准I/O-fopen-新建文件权限 标准I/O-处理错误信息 标准I/O-错误信息处理-示例1 标准I/O-错误信息处理-示例2 文件的关闭 标准IO 文件的打开 打开就是占用资源 下列函数可用于打开一个…

历经70+场面试,我发现了大厂面试的套路都是···

今年的金三银四刚刚过去&#xff0c;我又想起了我在去年春招时面试了50余家&#xff0c;加上暑期实习面试了20余家&#xff0c;加起来也面试了70余场的面试场景了。 基本把国内有名的互联网公司都面了一遍&#xff0c;不敢说自己的面试经验很丰富&#xff0c;但也是不差的。 …

Kali-linux使用社会工程学工具包(SET)

社会工程学工具包&#xff08;SET&#xff09;是一个开源的、Python驱动的社会工程学渗透测试工具。这套工具包由David Kenned设计&#xff0c;而且已经成为业界部署实施社会工程学攻击的标准。SET利用人们的好奇心、信任、贪婪及一些愚蠢的错误&#xff0c;攻击人们自身存在的…

HNU-计算机系统-Challenge

Challenge 计科210X wolf 202108010XXX 本题是从属于第七次讨论课的个人题,听说做了有加分?我来试试。 下面是相关报告。 题目: C 语言的初学者第一个编写的 C 代码一般是如下所示的“ #include <stdio.h> int main() {printf("Hello, World!");

项目管理:有效的沟通对项目的成功至关重要

为实施有效的沟通&#xff0c;需要建立沟通管理计划同时理解什么是沟通&#xff0c;沟通的对象是谁&#xff0c;沟通的目标是什么&#xff0c;难度在哪里&#xff0c;并选择合适的沟通方式。 项目沟通是确保项目团队的相关信息能及时、正确地产生、收集、发布、储存和最终处理…

Java多线程异常处理

文章目录 一. 线程中出现异常的处理1. 线程出现异常的默认行为2. setUncaoughtExceptionHandler()方法处理异常3. setDefaultUncaoughtExceptionHandler()方法进行异常处理 二. 线程组内出现异常 一. 线程中出现异常的处理 1. 线程出现异常的默认行为 当单线程中初出现异常时…

LOTO示波器如何测试阻抗的频响曲线

LOTO示波器如何测试阻抗的频响曲线 模块的输入输出端口&#xff0c;在电路分析上&#xff0c;一般简单表征为电阻来进行计算和分析。但多数情况下&#xff0c;这些端口并不是纯电阻的特性&#xff0c;更精确一些&#xff0c;它可能是电阻电容以及电感的组合&#xff0c;表现为非…

ChatGPT 聊天接口API 使用

一、准备工作 1.准备 OPENAI_ACCESS_TOKEN 2.准备好PostMan 软件 二、测试交流Demo 本次使用POSTMAN工具进行快速测试&#xff0c;旨在通过ChatGPT API实现有效的上下文流。在测试过程中&#xff0c;我们发现了三个问题&#xff1a;    1.如果您想要进行具有上下文的交流&…