C++11 / C++14 中 tuple 的基本使用

1,524 阅读2分钟

「这是我参与11月更文挑战的第 7 天,活动详情查看:2021最后一次更文挑战」。

参加该活动的第 14 篇文章

正文

tuple 是一组有序的值 (an ordered set of values) 。在关系数据库中,某个表格的一行也可以称为 tuple ;而在编程语言中,tuple 也可以用来储存从数据库表格中检索出来的一行。

pair 和 tuple 的代码对比示例

对 C++ 编程来说,pair 应该不陌生。tuple 是一个固定大小的不同类型值的集合,而 tuple 可视为 pair 的一般化,。比较麻烦的是用 tuple 时必须使用 std::get 来取得 tuple 內的值。


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

/// @note 
void UsePair() {
    std::pair<int, std::string> p1 = { 1, "kenny" };
    std::pair<float, char> p2 = std::make_pair(15.6f, 'b');

    std::cout << p1.first << ", " << p1.second << std::endl;
    std::cout << p2.first << ", " << p2.second << std::endl;
}

/// @note 
/// 使用 make_tuple 构造 tuple
/// 使用 get 获取指定 tuple 元素
void UseTuple() {
    std::tuple<int, double, std::string> t1(1, 15.4, "James");
    std::cout << std::get<0>(t1) << std::endl;
    std::cout << std::get<1>(t1) << std::endl;
    std::cout << std::get<2>(t1) << std::endl;
    
    /// @note 构造一个 tuple
    std::tuple<int, float, char> t2 = std::make_tuple(2, 35.2f, 'a');
    std::cout << std::get<0>(t2) << std::endl;
    std::cout << std::get<1>(t2) << std::endl;
    std::cout << std::get<2>(t2) << std::endl;
}

void main() {
    UsePair();
    UseTuple();
}

使用 std::tie 获取 tuple 的值

使用 std::tie,它会创建一个元组的左值引用。如下例所示,通过 tie 解包后,mytuple 中三个值会自动赋值给 tie 中的前后两个变量(我们如果只想解某个位置的值时,可以用 std::ignore 占位符来表示不解某个位置的值)。

    std::tuple<int, float, char> mytuple;

    mytuple = std::make_tuple(10, 2.6, 'a');          // packing values into tuple

    int myint;
    char mychar;
    std::tie(myint, std::ignore, mychar) = mytuple;   // unpacking tuple into variables

    std::cout << "myint contains: " << myint << '\n';
    std::cout << "mychar contains: " << mychar << '\n';

使用 std::forward_as_tuple 创建 tuple 的方法

使用 std::forward_as_tuple 创建的是右值引用的 tuple

示例如下

std::map<int, std::string> m;
m.emplace(std::piecewise_construct,
          std::forward_as_tuple(10),
          std::forward_as_tuple(20, 'a'));

以上代码实际上创建了一个类似于 std::tuple<int&&, std::string&&> 类型的 tuple

连接多个 tuple

示例如下

int main()
{
    std::tuple<int, std::string, float> t1(10, "Test",
                                           3.14);
    int n = 7;
    auto t2 = std::tuple_cat(t1, std::make_pair("Foo", "bar"), t1, std::tie(n));
    n = 10;
    print(t2);
}

输出结果为

(10, Test, 3.14, Foo, bar, 10, Test, 3.14, 10)

像初始化 vector 一样初始化 tuple

以下代码使用于 C++ 14 [VS2015/VS2017/VS2019 均测试通过]

在 C++ 11 中,我们可以通过 std::vector v = {1, 2, 3, 4}; 方便地初始化 vector 。但是 tuple 却必须要到 C++ 14 以上才会支持这种初始化的方式。所以在低于 C++ 14 的编译器上上面的示例会产生编译错误。


#include <iostream>
#include <tuple>

void UseTuple() {
    /// @note 需要支持 C++ 14 以上才能编译通过
    std::tuple<int, float, char> t3 = { 2, 35.2f, 'a' };
    std::cout << std::get<0>(t3) << std::endl;
    std::cout << std::get<1>(t3) << std::endl;
    std::cout << std::get<2>(t3) << std::endl;
}

/// @note 返回 2 个或以上的返回值
std::tuple<int, int> ReturnTuple() {
    /// @note 需要支持 C++ 14 以上才能编译通过
    return { 5, 6 };

    /// @note 如果是 C++ 11, 我们就只能使用以下形式
    //return std::make_tuple(5, 6);
}

void main() {
    UseTuple();
    ReturnTuple();
}

利用 tuple 获取结构体内部的数据类型

结构体定义了一个 Get 方法来返回其字段的基本信息

struct A
{
    int x;
    double y;

    auto Get()->decltype(std::make_tuple(x,y))
    {
        //const type_info& nInfo = typeid(decltype(std::make_tuple(x, y)));
        //std::cout << nInfo.name()<< std::endl;
        return std::make_tuple(x,y);    
    }
};

引申

高级的 tuple 使用方法可以参考 C++11 / C++14 中 tuple 的高级使用

更多 tuple 相关操作可以参考 cppreference.com