C/C++编程:元组tuple

1060 篇文章 297 订阅

了解过python的程序员都应该知道元素的概念,纵观传统C++中的容器,除了std::pair外,似乎没有现成的结构能够用来存放不同类型的数据(通常我们会自己定义结构),但std::pair只能保留两个元素。因此C++11引入了tuple扩展了pair的概念,能够拥有任意数量的元素

元组是”多元数组“的缩写

#include <tuple>
#include <iostream>
#include <variant>

auto get_student(int id)
{
    if (id == 0)
        return std::make_tuple(3.8, 'A', "John");
    if (id == 1)
        return std::make_tuple(2.9, 'C', "Jack");
    if (id == 2)
        return std::make_tuple(1.7, 'D', "Ive");
    // return type is std::tuple<double, char, std::string>
    return std::make_tuple(0.0, 'D', "null");
}

template <size_t n, typename... T>
constexpr std::variant<T...> _tuple_index(const std::tuple<T...>& tpl, size_t i) {
    if constexpr (n >= sizeof...(T))
        throw std::out_of_range("out of range.");
    if (i == n)
        return std::variant<T...>{ std::in_place_index<n>, std::get<n>(tpl) };
    return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(tpl, i);
}
template <typename... T>
constexpr std::variant<T...> tuple_index(const std::tuple<T...>& tpl, size_t i) {
    return _tuple_index<0>(tpl, i);
}

template <typename T>
auto tuple_len(T &tpl) {
    return std::tuple_size<T>::value;
}

template <typename T0, typename ... Ts>
std::ostream & operator<< (std::ostream & s, std::variant<T0, Ts...> const & v) { 
    std::visit([&](auto && x){ s << x;}, v); 
    return s;
}

int main()
{
    auto student = get_student(0);
    std::cout << "ID: 0, "
              << "GPA: "   << std::get<0>(student) << ", "
              << "Grade: " << std::get<1>(student) << ", "
              << "Name: "  << std::get<2>(student) << '\n';
    
    double gpa;
    char grade;
    std::string name;
    
    // tuple unpack
    std::tie(gpa, grade, name) = get_student(1);
    std::cout << "ID: 1, "
              << "GPA: "   << gpa   << ", "
              << "Grade: " << grade << ", "
              << "Name: "  << name  << '\n';
    
    
    std::tuple<std::string, double, double, int> t("123", 4.5, 6.7, 8);
    std::cout << std::get<std::string>(t) << std::endl;
    // std::cout << std::get<double>(t) << std::endl;   // illegal, runtime error
    std::cout << std::get<3>(t) << std::endl;
    
    // concat
    auto new_tuple = std::tuple_cat(get_student(1), std::move(t));
    
    // iteration
    for(int i = 0; i != tuple_len(new_tuple); ++i) {
        std::cout << tuple_index(new_tuple, i) << std::endl; // runtime indexing
    }
}
© 2021 GitHub, Inc.
    //创建一个四元素的元组
    // -元素初始化为默认值(基本类型为0)
    tuple<string,int,int,complex<double>> t;
    //创建并初始化一个元组
    tuple<int,float,string> t1(41,6.3,"nico");

    //使用make_tuple()创建元组:自动根据value建立tuple,因此不需要明确指出元素的类型
    auto t2 = make_tuple(22,44,"nico");

    //将t2中的第二个值赋给t1
    get<1>(t1) = get<1>(t2);

    //比较和赋值
    // -包含从tuple到tuple的类型转换
    if (t1 < t2) {  // compares value for value
        t1 = t2;    // OK, assigns value for value
    }

 

 

#include <iostream>
#include <tuple>
#include <complex.h>

using namespace std;

int  main()
{

    char ch = 'x'; short sh = 12; int num = 1234567; double db = 123; const char *p = "abc"; string s;
    tuple <char, short, int, double, const char * , string &>mytuple(ch, sh, num, db, p, s);

    // "iterate"遍历元素: tuple不是寻常的元素,不允许迭代元素[数组中唯一的例外]。可以使用成员函数get来处理元素
    // 因此必须在编译期就知道你打算处理的元素的索引
    auto autov0 = get<0>(mytuple); auto autov1 = get<1>(mytuple);
    auto autov2 = get<2>(mytuple); auto autov3 = get<3>(mytuple);
    auto autov4 = get<4>(mytuple); auto autov6 = get<5>(mytuple) = "hello";

    cout << autov0 << endl;cout << autov1 << endl; cout << autov2 << endl;
    cout << autov3 << endl;cout << autov4 << endl; cout << autov6 << endl;


    cin.get();
}

 

tuple与引用

#include <iostream>
#include <tuple>
#include <complex.h>
#include <functional>

using namespace std;

int  main()
{

    std::string s;

    auto x = std::make_tuple(s);
    std::get<0>(x) = "hello world";

    printf("s= %s, x = %s\n", s.c_str(), std::get<0>(x).c_str()) ;  //s= , x = hello world

    s = "";
    auto y = std::make_tuple(ref(s));
    std::get<0>(y) = "hello world";

    printf("s= %s, x = %s\n", s.c_str(), std::get<0>(y).c_str()) ;  //s= hello world, x = hello world



    cin.get();
}

提取tuple中的元素

#include <iostream>
#include <tuple>
#include <complex.h>
#include <functional>

using namespace std;

int  main()
{

    tuple<int,float,string> t1(41,6.3,"nico");
    int i; float f; std::string s;

    //method 1:
    std::make_tuple(std::ref(i), std::ref(f), std::ref(s)) = t1;
    printf("%d, %f, %s\n", i, f, s.c_str());

    // method 2:
    i = 0; s = "", f = 0.0;
    std::tie(i, std::ignore, s) = t1;
    printf("%d, %f, %s\n", i, f, s.c_str());

    cin.get();
}

tuple输出到output stream

 

tuple最初公开于boost,但是boost中可以将元素写到output stream, 而C++标准库不支持这个功能。除非你有如下头文件:

//printtuple.hpp

#include <tuple>
#include <iostream>

// helper: print elements with index IDX and higher of tuple t having MAX elements
template <int IDX, int MAX, typename... Args>
struct PRINT_TUPLE {
  static void print (std::ostream& strm, const std::tuple<Args...>& t) {
    strm << std::get<IDX>(t) << (IDX+1==MAX ? "" : ",");
    PRINT_TUPLE<IDX+1,MAX,Args...>::print(strm,t);
  }
};

// partial specialization to end the recursion
template <int MAX, typename... Args>
struct PRINT_TUPLE<MAX,MAX,Args...> {
  static void print (std::ostream& strm, const std::tuple<Args...>& t) {
  }
};

// output operator for tuples
template <typename... Args>
std::ostream& operator << (std::ostream& strm,
                           const std::tuple<Args...>& t)
{
    strm << "[";
    PRINT_TUPLE<0,sizeof...(Args),Args...>::print(strm,t);
    return strm << "]";
}

 

使用方法:

#include <tuple>
#include <iostream>
#include <string>
using namespace std;

int main()
{
    tuple <int,float,string> t(77,1.1,"more light");
    cout << "io: " << t << endl;
}

tuple与初值列

各个构造函数中,”接受不定个数的实参“被声明为explicti:

这是为了避免单一值被隐式转换为“带着一个元素”的tuple:

 

tuple与其他容器使用上的不同:

 

但是对pair<>和容器(除了array)是行得通的

对于tuple,正确的做法是明确将初值转换为一个tuple,比如运用make_tuple

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值