6. std::tuple
6.1 基本概述

std::tuple 是C++11引入的一个模板类,它允许将多个不同类型的值组合成一个单一的对象。类似于结构体,但不需要预先定义类型名称。tuple(元组)是一个固定大小的异构值集合,可以包含不同类型的元素。

  • 它是 std::pair 的泛化版本,pair 只能保存两个元素,而 tuple 可以保存任意数量的元素。
  • 本质是编译期定长、运行时不可变长的“通用结构体”。
6.2 用法

① 创建 Tuple

#include <tuple>

int main() {
 // 创建⼀个包含3个元素的tuple: int, double, string 
 std::tuple<int, double, std::string> t1(10, 3.14, "hello");
 // 使用 make_tuple 自动推导类型 
 auto t2 = std::make_tuple(20, 2.718, "world");
 // C++17起可以使用类模板参数推导 
 std::tuple t3(30, 1.618, "cpp"); // ⾃动推导为tuple<int, double, const char*>
 return 0;
}
  • 可存放任意可复制/可移动类型;
  • 大小在编译期固定,内存布局连续(与具体实现有关)。

② 元素访问:get<> + 结构化绑定

// 1. 索引版本
int&    i = std::get<0>(t1);	// 10
double& d = std::get<1>(t1);	// 3.14

// 2. 类型版本(唯一)
std::string& s = std::get<std::string>(t);	// hello

// 3. 修改
std::get<0> (t1) = 100;	// 修改第一个元素

// 4. C++14 起可以通过类型访问(类型必须唯一)
std::cout << std::get<int>(t1) << std::endl;
std::cout << std::get<double>(t1) << std::endl;

// 5. C++17 结构化绑定
auto [id, score, name] = t;   // 编译期展开,零开销
  • 索引必须是编译期常量;越界 → 编译错误。
  • get<T> 要求类型 T 在 tuple 里唯一。

③ 创建与拼接

工厂函数说明
std::make_tuple(...)自动推导类型,避免手写长模板实参
std::tie(...)生成“左值引用 tuple”,用于多值返回/比较
std::tuple_cat(...)把多个 tuple 拼接成一个更大的 tuple

示例:

int a = 1, b = 2;
auto t1 = std::tie(a, b);        // tuple<int&, int&> 
auto t2 = std::tuple_cat(t1, std::make_tuple(3.14));
// t2 类型 ≈ tuple<int&, int&, double>

// 解包
int x, y;
double z;
std::tie(x, y, z) = t2;

④ 比较与哈希

  • 支持 ==、!=、<、<=、>、>=——按字典序逐元素比较。
  • C++20 起支持 std::hash<std::tuple<...>>(需逐个元素可哈希)。
6.3 典型用途
  1. 多值返回

std::tuple<bool, std::string, int> parse(const std::string& line);
auto [ok, name, age] = parse(line);

     2. 一次性遍历多个容器

for (auto [it1, it2, idx] = std::tuple{v1.begin(), v2.begin(), 0};
     it1 != v1.end() && it2 != v2.end();
     ++it1, ++it2, ++idx) { /* ... */ }

    3.作为可变模板“容器”
       模板元编程里常把参数包存进 tuple 再逐元素处理。

    4.替代小结构体
       就地一次性打包,无需手写 struct;配合结构化绑定可读性极高。

6.4 与 std::pair 的关系
  • pair 只能存 2 个元素,有 .first/.second;
  • tuple 可存任意数量,没有名字成员,全靠 get<> / 结构化绑定;
  • 接口风格一致:make_pair vs make_tuple,tuple_cat 也能拼接 pair。
auto t = std::make_tuple(1, 2.0, 3L);
std::apply([](auto... args){ ((std::cout << args << ' '), ...); }, t);
  • std::apply 把 tuple 元素解包成函数实参;
  • 配合 std::index_sequence 可自行实现遍历、累加、打印等元函数。
.5 常见陷阱
  • 元素默认构造:内置类型默认初始化为未定义值(若未显式提供)。
  • 拷贝开销大:元素多且大对象时,按值传递 tuple 会深拷贝,考虑 std::ref 或 std::move。
  • 编译期索引:get<i> 的 i 必须是常量表达式,运行时下标无法实现。

更多推荐