C++11 tuple的使用

这篇具有很好参考价值的文章主要介绍了C++11 tuple的使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

C++中的元组(tuple)是一个类似于 pair 的标准库模板类(template class),它是一个固定大小且可以包含不同类型的值的容器。

元组可以用于组合和处理多个值,类似于结构体(struct),但不需要定义新的类型。元组的长度和每个元素的类型都是在编译时确定的。由于已经在编译期间已经确定元组的大小,所以不能在后续扩充 tuple 的大小了。

C++中的元组(tuple)是在C++11标准中引入的。在此之前,C++标准库中没有元组的实现,但是一些第三方库和框架提供了自己的元组实现。

C++11标准引入了std::tuple模板类,它定义了一个固定大小且可以包含不同类型的值的容器。std::tuple是一个非常有用的工具,可以用于组合和处理多个值,以及在函数返回多个值、遍历多个容器和参数包展开等场景中使用。

除了std::tuple之外,C++11标准还引入了std::make_tuple和std::tie函数,它们可以方便地创建和解包元组。C++11标准库中还引入了一些其他的元组相关功能,如std::tuple_size和std::tuple_element等,用于获取元组的大小和元素类型。

当我们需要将一些数据组合成单一的对象,但又不想定义一个新的数据结构来表示这些数据时, tuple 便适用于此场景。

接下来的示例我都是参考chatgpt。

一、tuple的使用

1.1 基本使用

(1)

#include <tuple>
#include <string>
#include <iostream>

int main() {
    
    //初始化 tuple :myTuple
    //std::tuple<int, std::string, double> myTuple(42, "hello", 3.14);
    std::tuple<int, std::string, double> myTuple;
    //使用 make_tuple 初始化 tuple
    myTuple = std::make_tuple(42, "hello", 3.14);
    
    //tuple 成员都是未命名的,使用 std::get()函数访问 tuple 成员:get<i>(tuple)
    std::cout << std::get<0>(myTuple) << std::endl;    // 42
    std::cout << std::get<1>(myTuple) << std::endl;    // "hello"
    std::cout << std::get<2>(myTuple) << std::endl;    // 3.14
    
    //使用 tuple_size 获取 tuple 中元素的个数
	std::cout << std::tuple_size<decltype(myTuple)>::value << std::endl;   //3
    return 0;
}

上面的代码创建了一个包含三个元素的元组,并使用get函数分别访问每个元素的值。元组的元素可以使用索引或者std::get函数来访问。

(2)
元组还可以使用std::tie函数将元素解包到不同的变量中:

#include <tuple>
#include <string>
#include <iostream>

int main() {

    std::tuple<int, std::string, double> myTuple(42, "hello", 3.14);
    std::cout << std::get<0>(myTuple) << std::endl;    // 42
    std::cout << std::get<1>(myTuple) << std::endl;    // "hello"
    std::cout << std::get<2>(myTuple) << std::endl;    // 3.14

    int myInt;
    std::string myString;
    double myDouble;
    //使用 tie 将 myTuple这将元组的每个元素分别赋值给myInt、myString和myDouble变量。
    std::tie(myInt, myString, myDouble) = myTuple;    
    std::cout << myInt << std::endl;        // 42
    std::cout << myString << std::endl;     // "hello"
    std::cout << myDouble << std::endl;     // 3.14

    return 0;
}

1.2 tuple_element

std::tuple_element是一个类型别名模板,用于获取std::tuple中指定位置的元素类型。

#include <iostream>
#include <tuple>
#include <type_traits>

int main() {
    // 创建一个std::tuple对象
    std::tuple<int, double, std::string> myTuple(42, 3.14, "hello");

    // 获取元组的第一个元素
    int x = std::get<0>(myTuple);
    std::cout << "x = " << x << std::endl;

    // 获取元组的第二个元素
    double y = std::get<1>(myTuple);
    std::cout << "y = " << y << std::endl;

    // 获取元组的第三个元素
    std::string z = std::get<2>(myTuple);
    std::cout << "z = " << z << std::endl;

    // 检查元素类型
    // std::tuple_element<0, decltype(myTuple)>::type表示获取myTuple的第一个元素的类型,即int类型
    std::cout << "type of 1st element: " << typeid(std::tuple_element<0, decltype(myTuple)>::type).name() << std::endl;
    std::cout << "type of 2nd element: " << typeid(std::tuple_element<1, decltype(myTuple)>::type).name() << std::endl;
    std::cout << "type of 3rd element: " << typeid(std::tuple_element<2, decltype(myTuple)>::type).name() << std::endl;

    // 使用std::tuple_element_t类型别名获取元素类型
    static_assert(std::is_same_v<std::tuple_element_t<0, decltype(myTuple)>, int>);
    static_assert(std::is_same_v<std::tuple_element_t<1, decltype(myTuple)>, double>);
    static_assert(std::is_same_v<std::tuple_element_t<2, decltype(myTuple)>, std::string>);

    return 0;
}

其中:

typeid(std::tuple_element<0, decltype(myTuple)>::type).name() 

typeid是一个C++运算符,用于获取一个表达式的类型信息。当应用于一个类型时,typeid返回一个std::type_info对象,表示该类型的类型信息。而std::type_info::name()函数用于获取该类型的名称。

在这段代码中,typeid(std::tuple_element<0, decltype(myTuple)>::type)用于获取myTuple的第一个元素的类型,即int类型的std::type_info对象。而std::type_info::name()函数用于获取int类型的名称,即字符串"int"。

类似地,typeid(std::tuple_element<1, decltype(myTuple)>::type).name()用于获取myTuple的第二个元素的类型名称,即字符串"double";而typeid(std::tuple_element<2, decltype(myTuple)>::type).name()用于获取myTuple的第三个元素的类型名称,即字符串"std::string"。

总之,typeid运算符和std::type_info类是C++标准库中用于获取类型信息的工具,它们可以帮助我们在运行时查询和操作类型信息。在程序中使用typeid运算符和std::type_info类可以帮助我们进行一些类型安全检查和调试任务。

其中:

static_assert(std::is_same_v<std::tuple_element_t<0, decltype(myTuple)>, int>);

使用了C++11中的static_assert断言,用于在编译时检查一个表达式是否为true,如果不是,则会在编译时产生一个编译错误。

在这段代码中,static_assert用于检查std::tuple_element_t<0, decltype(myTuple)>是否等于int类型。其中,std::tuple_element_t<0, decltype(myTuple)>表示获取myTuple的第一个元素的类型,即int类型别名。而std::is_same_v是一个类型比较模板,用于检查两个类型是否相同,如果相同则返回true,否则返回false。

因此,如果std::tuple_element_t<0, decltype(myTuple)>等于int类型,则static_assert不会产生编译错误,程序正常编译和运行;否则,static_assert会产生编译错误,提示用户类型不匹配。

这种在编译期间进行静态检查的技术可以帮助我们在编译时发现代码中的错误,从而提高代码的健壮性和可维护性。

比如我把上述改为:

static_assert(std::is_same_v<std::tuple_element_t<0, decltype(myTuple)>, long>);
$ g++ tuple.cpp 
tuple.cpp: In function ‘int main():
tuple.cpp:28:24: error: static assertion failed
   28 |     static_assert(std::is_same_v<std::tuple_element_t<0, decltype(myTuple)>, long>);
      |                   ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

编译期间报错。

1.3 tuple引用的使用

tuple可以通过引用(reference)来访问和修改元组中的元素。

#include <iostream>
#include <tuple>
#include <functional>

int main() {
    int x = 42;
    double y = 3.14;

    // 创建一个std::tuple对象,存储x和y的引用
    std::tuple<std::reference_wrapper<int>, std::reference_wrapper<double>> myTuple(std::ref(x), std::ref(y));

    // 修改元组中的元素
    std::get<0>(myTuple).get() = 100;
    std::get<1>(myTuple).get() = 2.718;

    // 输出原始变量的值
    std::cout << "x = " << x << std::endl;
    std::cout << "y = " << y << std::endl;

	//修改x,y的值
    x = 10;
    y = 1.1;
    //tuple元素中的值也发生了相应的变换
    std::cout << std::get<0>(myTuple) << std::endl;    // 10
    std::cout << std::get<1>(myTuple) << std::endl;    // 1.1  

    return 0;
}

std::ref 是一个函数模板,它接受一个对象的引用,并返回一个包装(wrapper)该引用的 std::reference_wrapper 对象。这样,我们就可以将这个 std::reference_wrapper 对象作为元素插入到 std::tuple 中。

首先创建了两个变量x和y,然后创建了一个std::tuple对象myTuple,存储了x和y的引用。在创建myTuple时,我们使用了std::ref函数将x和y转换为了std::reference_wrapper类型,即引用类型的包装器。然后,我们使用std::get函数和std::reference_wrapper::get函数分别访问和修改了myTuple中的元素。最后,我们输出了原始变量x和y的值,可以看到它们已经被修改为了std::tuple中的值。

需要注意的是,当我们使用std::ref函数创建std::tuple时,需要使用std::reference_wrapper类型来存储引用,因为std::tuple通常不直接存储引用类型。在访问std::tuple中的元素时,需要使用std::reference_wrapper::get函数来获取原始的引用变量。

std::tuple可以存储引用类型,但需要注意引用的生命周期问题。
当我们将一个引用类型的变量作为std::tuple的元素时,实际上是将该引用的值存储在std::tuple中。如果该引用的原始变量在std::tuple被访问之前被销毁了,那么在访问std::tuple时就会出现未定义行为。在访问std::tuple中的引用元素时,我们需要确保引用的原始变量的生命周期足够长,以避免未定义行为。通常情况下,我们应该避免在std::tuple中存储引用类型,并使用std::reference_wrapper类型代替引用类型来存储元素的引用。

总之,使用std::ref函数可以方便地将变量转换为std::tuple的元素引用,从而方便地访问和修改元素。同时,使用std::reference_wrapper类型可以避免在std::tuple中存储引用类型的问题。

二、c++17结构化绑定

1.结构化绑定简介

C++17引入了结构化绑定(Structured Bindings)特性,它允许我们使用一种更简洁的语法来分解(解包)一个复杂类型的成员变量。结构化绑定通过将一个复杂类型的成员变量分解为多个单独的变量,使得我们可以更方便地访问和处理这些变量。

原则上,结构化绑定可以用于公有成员,原始C-style数组,以及“似若tuple”的对象(比如:std::pair,std::tuple和std::array):

下面是一个简单的示例:

#include <iostream>
#include <tuple>

//x,y是结构体的公有成员
struct MyStruct {
    int x;
    double y;
};

int main() {
    MyStruct myObj {42, 3.14};

    // 使用结构化绑定分解myObj成为单独的变量x和y
    auto [x, y] = myObj;

    // 输出分解后的变量
    std::cout << "x = " << x << std::endl;
    std::cout << "y = " << y << std::endl;

    return 0;
}

结构化绑定的好处是可以直接通过名字访问值,并且由于名字可以传递语义信息,使得代码可读性也大大提高。

在上面的示例中,我们首先定义了一个结构体MyStruct,其中包含两个成员变量x和y。然后,我们创建了一个MyStruct对象myObj,包含了一些随机的值。接着,我们使用结构化绑定将myObj分解为两个单独的变量x和y。最后,我们输出了分解后的变量x和y的值。

需要注意的是,结构化绑定只能用于支持std::tuple-like接口的类型(例如std::pair和std::tuple),或者可以使用std::get函数进行访问的类型。如果我们想使用结构化绑定来分解自定义的类型,需要在该类型中实现std::tuple-like接口或者提供std::get函数的实现。

总之,结构化绑定是C++17引入的一项非常方便的特性,它可以帮助我们更方便地访问和处理复杂类型的成员变量。

2.tuple 使用结构化绑定

#include <iostream>
#include <tuple>

int main() {
    std::tuple<int, double, std::string> myTuple(42, 3.14, "Hello, world!");

    // 使用结构化绑定分解myTuple成为单独的变量x, y, z
    auto [x, y, z] = myTuple;

    // 输出分解后的变量
    std::cout << "x = " << x << std::endl;
    std::cout << "y = " << y << std::endl;
    std::cout << "z = " << z << std::endl;

    return 0;
}

在上面的示例中,我们首先创建了一个std::tuple对象myTuple,包含了三个元素。然后,我们使用结构化绑定将myTuple分解为三个单独的变量x、y和z。最后,我们输出了分解后的变量的值。

三、tuple返回多个值

使用std::tuple来返回多个值可以使得代码更加简洁和易于理解。
例子:

#include <iostream>
#include <tuple>

std::tuple<int, double, std::string> myFunction() {
    int x = 42;
    double y = 3.14;
    std::string z = "Hello, world!";

    return std::make_tuple(x, y, z);
}

int main() {
    // 调用myFunction函数,获取返回值
    // 使用结构化绑定特性
    auto [x, y, z] = myFunction();

    // 输出返回值
    std::cout << "x = " << x << std::endl;
    std::cout << "y = " << y << std::endl;
    std::cout << "z = " << z << std::endl;

    return 0;
}

在上面的示例中,我们定义了一个函数myFunction,该函数返回一个std::tuple对象,包含了三个不同类型的值。然后,我们在主函数中调用myFunction函数,并使用结构化绑定将返回值分解为三个单独的变量x、y和z。最后,我们输出了分解后的变量的值。

需要注意的是,使用std::tuple返回多个值可能会影响代码的可读性。因此,我们应该在需要时选择合适的返回类型,以使代码更加清晰易懂。

总结

元组(tuple)的使用有其优点和缺点。下面是一些主要的优点和缺点:

优点:

元组可以容纳任意数量和类型的元素,因此它们非常灵活。这意味着您可以使用它们来存储和处理多个值,而不必定义新的类型或容器。

元组可以通过std::get函数或使用类似于结构体的点表示法来访问其元素。这使得元组使用起来非常方便。

元组可以在函数中作为返回值,以便返回多个值。这使得函数的返回值更加清晰和易读。

缺点:

元组的元素可以通过std::get函数和索引访问,但不能像结构体一样使用成员变量名。这使得代码可读性稍有降低。

元组的长度和每个元素的类型都是在编译时确定的。这意味着您无法在运行时动态添加或删除元素。

元组的元素可以包含不同的类型,这意味着您需要小心处理类型转换和类型匹配的问题。

参考资料

Chatgpt
https://github.com/CnTransGroup/Cpp17TheCompleteGuideChinese/blob/master/src/part1/cp1.md
https://www.zhihu.com/question/298981020文章来源地址https://www.toymoban.com/news/detail-427795.html

到了这里,关于C++11 tuple的使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 《Python入门到精通》元组 Tuple 详解,元组常用函数

    「作者主页」: 士别三日wyx 「作者简介」: CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」: 小白零基础《Python入门到精通》 「元组」 使用小括号 () 包裹, 「元素」 之间使用逗号 , 间隔。元组与列表相似,但元组的元素 「不可变」

    2024年02月15日
    浏览(52)
  • 08列表(list)与元组(tuple)

    [数据1,数据2,数据3,数据4,......] 列表可以存储多个数据,数据之间的逗号以英文分割而且可以数据是不同类型的数据,列表是可变数据类型。 空列表 list_data = [] 或者 list_data = list() 列表的创建 列表的作用是⼀次性存储多个数据,程序员可以对这些数据进行的操作有:增、删、

    2023年04月12日
    浏览(35)
  • 一文掌握python常用的tuple(元组)操作

    目录 1. 元组的创建 2. 访问元组元素 3. 切片操作 4. 合并元组 5. 复制元组 6. 内置函数与方法 7. 元组拆分 8. 星号表达式在元组中的应用 9. 遍历元组 10. 元组的嵌套 11. 元组与条件判断和逻辑运算 12. 常见应用场景 13. 元组与枚举(Enum) 14. 元组推导式 15. 元组与匿名函数(Lambd

    2024年03月09日
    浏览(46)
  • python元组tuple添加元素的两种方法

    方法一 :可以使用python内置的__add__()方法,使用该方法为python元组添加元素时,需要使用元组来对象来进行调用,然后将需要添加的元素,以元组的数据类型进行传递,该方法并需修改调用对象,而是以新的元组进行返回,具体可见下方的实例代码。 方法二 :将python的元组

    2024年02月12日
    浏览(38)
  • Python:list列表与tuple元组的区别

    在Python中, List(列表) 和 Tuple(元组) 都是用于 存储一组有序元素的数据结构 ,但它们有一些关键的区别,包括可变性、性能、语法等方面。 用法: 用方括号[]表示。 支持增删改操作,是可变的。 适用于存储有序的、可变的元素集合。 示例: 用法: 用圆括号()表示。

    2024年01月18日
    浏览(76)
  • Python教程(10)——Python变量类型元组tuple的详细用法

    在Python中,元组(Tuple)是一种有序且不可变的数据类型。元组可以包含任意数量的元素,用逗号分隔,并用圆括号括起来。与列表(List)不同,元组的元素不能修改。元组与列表一样,可以通过索引访问其中的元素。 元组的不可变性意味着无法向元组中添加、删除或修改元

    2024年02月12日
    浏览(33)
  • Python列表(list)、元组(tuple)和字典(dictionary)的区别

    目录 列表(list)  访问列表元素  更新和删除列表元素 元组(tuple) 元组的访问、更新、删除操作  字典(dictionary)  创建空字典 修改、删除字典 总结 列表(list)、元组(tuple)和字典(dictionary)都是序列,序列都是由索引和元素组成。遍历和访问都按照如下格式: 具

    2023年04月13日
    浏览(46)
  • C++11 tuple的使用

    C++中的元组(tuple)是一个类似于 pair 的标准库模板类(template class),它是一个固定大小且可以包含不同类型的值的容器。 元组可以用于组合和处理多个值,类似于结构体(struct),但不需要定义新的类型。元组的长度和每个元素的类型都是在编译时确定的。由于已经在编

    2024年02月01日
    浏览(40)
  • Python数据容器(列表list、元组tuple、字符串str、字典dict、集合set)详解

    相关介绍: 一种可以容纳多份数据的数据类型,容纳的每一份数据称之为一个元素。每一个元素,可以是任意类型的数据 分为五类: 列表[list]、元组(tuple)、字符串(str)、集合{set}、字典{dict} 相应区别: 列表 元祖 字符串 集合 字典 元素数量 多个 多个 多个 多个 多个 元素类

    2024年02月11日
    浏览(82)
  • C++(11):判断tuple是否含有某个类型

    有的时候需要判断tuple中是否有个某个类型,可以借助变长模板的递归调用方式进行检查: C++(11):变长模板_变长模板参数 c++11-CSDN博客 另外还使用了true_type和false_type:

    2024年02月04日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包