C++11, 14, 17对tuple元素的访问详情

目录
  • C++11, 14, 17对tuple元素的访问
  • std::tuple大总结
    • 元组的使用
    • 典型使用

C++11, 14, 17对tuple元素的访问

std::tuple 作为可以存放任意个数,任意类型的元祖被我个人经常使用。

记得以前看侯捷谈到这个数据结构的时候,被他的实现所惊奇,太巧妙地设计。

我自己在使用std::tuple的时候也随着C++版本的更新尝试新的写法,对于元组中元素的获取,我一直觉得很有意思:

比如我有这么一个源文件:

#include <iostream>
#include <tuple>

#define CPP11 (__cplusplus < 201401L)
#define CPP14 (__cplusplus < 201701L and __cplusplus > 201401L)
#define CPP17 (__cplusplus >= 201702L)

class Moo {
  Moo() { ::printf("%s\n", __PRETTY_FUNCTION__); }
  Moo(Moo const &) { ::printf("%s\n", __PRETTY_FUNCTION__); }
  Moo(Moo &&) { ::printf("%s\n", __PRETTY_FUNCTION__); }
  Moo &operator=(Moo const &) noexcept {
    ::printf("%s\n", __PRETTY_FUNCTION__);
    return *this;
  }

  Moo &operator=(Moo &&) noexcept {
    ::printf("%s\n", __PRETTY_FUNCTION__);
    return *this;
  }

  ~Moo() { ::printf("%s\n", __PRETTY_FUNCTION__); }
};

int main() {
  std::cout << "c++ version:" << __cplusplus << std::endl;
  auto &&tp = std::make_tuple<Moo, const char *, int>(Moo(), "hello world", 3);

#if CPP11
  auto &&first = std::get<0>(tp);
  auto &&second = std::get<1>(tp);
  auto &&third = std::get<2>(tp);
#endif

#if CPP14
  auto &&first = std::get<Moo>(tp);
  auto &&second = std::get<const char *>(tp);
  auto &&third = std::get<int>(tp);
#endif

#if CPP17
  auto [a, b, c] = tp;
  auto &[first, second, third] = tp;
#endif

  return 0;
}

Moo类考察当前对象在构造tuple和返回值的过程中经历怎样的人生

  • C++11: 使用std::get方法来获取对应秩的元素的引用
  • C++14: 使用类似在nlohmannjson里面获取对应类型的json对象的方式,通过类型来获取对应的元素的引用,有点现代C++那味儿了吼
  • C++17: 引入了结构化绑定,不实用引用的时候会返回新创建的临时对象,a, b, c是对临时对象的右值引用,而使用引用则是想std::get那样引用原本对象。C++17结构化绑定yyds!表达力比之前强太多

std::tuple大总结

C++11引入了一个新的较实用的模板类型,std::tuple,也即是元组。元组是一个固定大小的不同类型(异质,heterogeneous)值的集合,也即它可以同时存放不同类型的数据。

类似于python中用小括号表示的元组类型。C++已有的std::pair类型类似于一个二元组,可看作是std::tuple的一个特例,std::tuple也可看作是std::pair的泛化。std::pair的长度限制为2,而std::tuple的元素个数为0~任意个。

元组的使用

  • 有时候希望将一些数据组合成单一对象,但又不想麻烦地定义一个新数据结构来表示这些数据时,std::tuple是非常有用的。我们可以将std::tuple看作一个“快速而随意”的数据结构。我们可以把它当作一个通用的结构体使用,但又不需要创建和获取结构体的特征,使得程序更加简洁直观。
  • 创建一个std::tuple对象时,可以使用tuple的默认构造函数,它会对每个成员进行值初始化;也可以为每个成员提供一个初始值,此时的构造函数是explicit的,因此必须使用直接初始化方法。
  • 一个std::tuple类型的成员数目是没有限制的,因此,元组的成员都是未命名的。要访问一个元组的成员,就要使用一个名为get的标准库函数模板。为了使用get,我们必须指定一个显式模板实参,它指出我们想要访问第几个成员。我们传递给get一个tuple对象,它返回指定成员的引用。get尖括号中的值必须是一个整型常量表达式。与往常一样,我们从0开始计数,意味着get<0>是第一个成员。
  • 为了使用tuple_size或tuple_element,我们需要知道一个元组对象的类型。与往常一样,确定一个对象的类型的最简单方法就是使用decltype。
  • std::tuple的一个常见用途是从一个函数返回多个值。
  • std::tuple中元素是被紧密地存储的(位于连续的内存区域),而不是链式结构。
  • std::tuple实现了多元组,这是一个编译期就确定大小的容器,可以容纳不同类型的元素。多元组类型在当前标准库中被定义为可以用任意数量参数初始化的类模板。每一模板参数确定多元组中一元素的类型。所以,多元组是一个多类型、大小固定的值的集合。

典型使用

创建和初始化

{
    std::tuple<int, double, std::string> first;    // 创建一个空的元组,需要指定元组元素的数据类型,调用各个成员的默认构造函数进行初始化。
    std::tuple<int, double, std::string> second(first);  // 拷贝构造
    std::tuple<int, char> third(10, 'a');        // 创建并初始化,使用小括号初始化
    std::tuple<int, std::string, double> fourth{42, "Test", -3.14};  // 创建并初始化,使用新的大括号初始化列表方式初始化
    std::tuple<int, char> fifth(std::make_tuple(20, 'b'));     // 移动构造,使用模板库的make_tuple
    first = std::make_tuple(1, 3.14, "tuple");       // 移动赋值
    int i_third = 3;
    std::tuple<int&> sixth(std::ref(i_third));   // 创建一个元组,元组的元素可以被引用
} 

元组的访问和修改

std::get<N>()

{
    int n = 1;
    auto t = std::make_tuple(10, "Test", 3.14, std::ref(n), n);
    // get尖括号中的值必须是一个整型常量表达式。从0开始计数,意味着get<0>是第一个成员。
    std::cout << "The value of t is "  << "("
               << std::get<0>(t) << ", " << std::get<1>(t) << ", "
               << std::get<2>(t) << ", " << std::get<3>(t) << ", "
               << std::get<4>(t) << ")\n";
    // 由于get返回指定元素的引用,所以可用来修改指定位置的元素的值。此处因为第4个元素是引用类型,所以被引用的值也会改变
    std::get<3>(t) = 9;
    std::cout << n << std::endl;
}

元组的元素个数

使用std::tuple_size<>()

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

元组的解包

std::tie() 元组包含一个或者多个元素,使用std::tie解包: 首先需要定义对应元素的变量,再使用tie。

{ // std::tie: function template, Constructs a tuple object whose elements are references
  // to the arguments in args, in the same order
  // std::ignore: object, This object ignores any value assigned to it. It is designed to be used as an
  // argument for tie to indicate that a specific element in a tuple should be ignored.
    
    int myint;
    char mychar;
 
    std::tuple<int, float, char> mytuple;
 
    mytuple = std::make_tuple(10, 2.6, 'a');          // packing values into tuple
 
    std::tie(myint, std::ignore, mychar) = mytuple;   // unpacking tuple into variables
 
    std::cout << "myint contains: " << myint << '\n';
    std::cout << "mychar contains: " << mychar << '\n';
}

元组的元素类型获取

获取元组中某个元素的数据类型,需要用到另外一个类型: std::tuple_element。 语法: std::tuple_element<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";
}

元组的拼接

使用 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 = -=+
}

元组的遍历

元组没用提供operator []重载,遍历起来较为麻烦,需要为其单独提供遍历模板函数

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • C++11新特性std::tuple的使用方法

    1. 引入头文件 #include <tuple> 2. std::tuple初始化 std::tuple<int, std::string, float> t1(10, "Test", 3.14); 这里要注意,不是所有的C++ 11编译器都支持copy-list-initialization的方式.如下代码所示. std::tuple<int, int> foo_tuple() { return {1, -1}; // Error until N

  • C++ STL 内 std::{bind/tuple/function} 简单实现

    基本逻辑思考 首先是实现 function,这个比较简单,重载 operator() 就好,这里只实现对函数指针的包装 其次是实现 tuple,这个会比较绕,通过模板取第一个参数,然后用剩下的参数继续生成  tuple并继承,是一种递归的思想 有了 tuple 就要有 get(),这个就更比较绕了,首先是需要类似的方式实现获得 tuple 的值类型与元组类型,然后通过强制类型转换,获取对应的层级的 value 接下来是 bind,首先要解决的就是如何保存创建时的参数列表,这里就用到 tuple

  • C++ tuple元组的基本用法(总结)

    1,元组简介 tuple是一个固定大小的不同类型值的集合,是泛化的std::pair.我们也可以把他当做一个通用的结构体来用,不需要创建结构体又获取结构体的特征,在某些情况下可以取代结构体使程序更简洁,直观.std::tuple理论上可以有无数个任意类型的成员变量,而std::pair只能是2个成员,因此在需要保存3个及以上的数据时就需要使用tuple元组了. tuple(元组)在c++11中开始引用的.tuple看似简单,其实它是简约而不简单,可以说它是c++11中一个既简单又复杂的东东,关于

  • C++11, 14, 17对tuple元素的访问详情

    目录 C++11, 14, 17对tuple元素的访问 std::tuple大总结 元组的使用 典型使用 C++11, 14, 17对tuple元素的访问 std::tuple 作为可以存放任意个数,任意类型的元祖被我个人经常使用. 记得以前看侯捷谈到这个数据结构的时候,被他的实现所惊奇,太巧妙地设计. 我自己在使用std::tuple的时候也随着C++版本的更新尝试新的写法,对于元组中元素的获取,我一直觉得很有意思: 比如我有这么一个源文件: #include <iostream> #inc

  • Pandas DataFrame中的tuple元素遍历的实现

    pandas中遍历dataframe的每一个元素 假如有一个需求场景需要遍历一个csv或excel中的每一个元素,判断这个元素是否含有某个关键字 那么可以用python的pandas库来实现. 方法一: pandas的dataframe有一个很好用的函数applymap,它可以把某个函数应用到dataframe的每一个元素上,而且比常规的for循环去遍历每个元素要快很多.如下是相关代码: import pandas as pd data = [["str","ewt"

  • C++11/14的新特性(更简洁)

    新的字符串表示方式--原生字符串(Raw String Literals) C/C++中提供了字符串,字符串的转义序列,给输出带来了很多不变,如果需要原生义的时候,需要反转义,比较麻烦. C++提供了,原生字符串,即字符串中无转义,亦无需再反义.详细规则见带码: #include <iostream> using namespace std; string path = "C:\Program Files (x86)\alipay\aliedit\5.1.0.3754";

  • C++11/14 线程的创建与分离的实现

    线程的创建 让我们看看示例代码(t1.cpp). #include <iostream> #include <thread> void thread_function() { std::cout << "thread function\n"; } int main() { std::thread t(&thread_function); // 线程 t 开始运行 std::cout << "main thread\n&q

  • C++11/14 线程调用类对象和线程传参的方法

    线程调用类对象 在前面的示例中,我们为线程任务使用了通常的函数.实际上,我们可以使用任何可调用对象或者lambda函数,如下调用类对象的例子: #include <iostream> #include <thread> class MyFunctor { public: void operator()() { std::cout << "functor\n"; } }; int main() { MyFunctor fnctor; std::thre

  • C++11/14 线程中使用Lambda函数的方法

    多线程中使用lambda 在本篇文章中,主要介绍lambda函数在多线程中的使用. 先从下面的例子开始吧: #include <iostream> #include <thread> int main() { std::thread t([]() { std::cout << "thread function\n"; }); std::cout << "main thread\n"; t.join(); return

  • python计算列表元素与乘积详情

    目录 插入代码块 列表乘积计算 插入代码块 使用sum函数: numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] print(sum(numbers)) 使用reduce函数: # 方式1 from functools import reduce numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] results = reduce(lambda x, y: x + y, numbers) print(results) # 方式2 f

  • Python入门篇之条件、循环

    1.if语句 Python 中的if子句看起来十分熟悉. 它由三部分组成: 关键字本身, 用于判断结果真假的条件表达式, 以及当表达式为真或者非零时执行的代码块. if 语句的语法如下: if expression: expr_true_suite if 语句的 expr_true_suite代码块只有在条件表达式的结果的布尔值为真时才执行, 否则将继续执行紧跟在该代码块后面的语句. (1)多重条件表达式 单个if语句可以通过使用布尔操作符and,or和not实现多重判断条件或是否定判断条件.

  • 深入理解NumPy简明教程---数组3(组合)

    前两篇文章对NumPy数组做了基本的介绍,本篇文章对NumPy数组进行较深入的探讨.首先介绍自定义类型的数组,接着数组的组合,最后介绍数组复制方面的问题. 自定义结构数组 通过NumPy也可以定义像C语言那样的结构类型.在NumPy中定义结构的方法如下: 定义结构类型名称:定义字段名称,标明字段数据类型. student= dtype({'names':['name', 'age', 'weight'], 'formats':['S32', 'i','f']}, align = True) 这里

随机推荐