C++ 中 std::tuple 使用详解
 
基本概念
std::tuple 是 C++11 引入的模板类,用于打包任意数量、任意类型的值在一起。可看作是类型安全的变长结构体。
#include <tuple>std::tuple<int, std::string, double> t(42, "hello", 3.14);
 
创建 tuple 的方法
auto t1 = std::make_tuple(1, 2.5, "abc");       // 自动推导类型
std::tuple<int, float, const char*> t2(1, 2.5f, "abc");
 
std::make_tuple 会自动进行类型推导和转化。
访问元素:std::get<>
 
#include <iostream>std::tuple<int, std::string, double> t(1, "hi", 3.14);
std::cout << std::get<1>(t) << std::endl;  // 输出 "hi"
 
注意:必须使用编译期常量作为索引,不能传运行时变量!
获取类型 & 大小
#include <tuple>
#include <type_traits>using MyTuple = std::tuple<int, float, std::string>;constexpr std::size_t size = std::tuple_size<MyTuple>::value;
using T1 = std::tuple_element<1, MyTuple>::type;  // float
 
修改元素值
std::get<2>(t) = 6.28;  // 修改 double 类型的值
 
拆解 tuple(结构化绑定)
C++17 提供结构化绑定:
auto [x, y, z] = t;  // 自动展开为三个变量
 
tuple 的比较、赋值
std::tuple<int, int> a(1, 2), b(1, 3);
if (a < b) std::cout << "a < b";  // 支持逐元素比较
 
高级技巧:递归访问 tuple
遍历 tuple 中所有元素(使用模板递归)
#include <iostream>
#include <tuple>template<std::size_t I = 0, typename... Ts>
void print_tuple(const std::tuple<Ts...>& t) {if constexpr (I < sizeof...(Ts)) {std::cout << std::get<I>(t) << " ";print_tuple<I + 1>(t);}
}int main() {auto t = std::make_tuple(1, 3.14, "hello");print_tuple(t);  // 输出: 1 3.14 hello
}
 
对每个元素执行函数(C++17 fold expression)
 
template<typename... Ts>
void apply_to_each(const std::tuple<Ts...>& t) {std::apply([](const auto&... args) {((std::cout << args << " "), ...);}, t);
}
 
根据类型访问 tuple 元素(要求类型唯一)
template<typename T, typename... Ts>
T& get_by_type(std::tuple<Ts...>& t) {return std::get<T>(t);
}std::tuple<int, double, std::string> t{42, 3.14, "hi"};
auto& str = get_by_type<std::string>(t);  // OK
 
⚠️ 不能有多个相同类型,否则编译失败!
与 std::tie 结合解包、忽略元素
 
int a; std::string b;
std::tuple<int, std::string, double> t{10, "hi", 3.14};std::tie(a, b, std::ignore) = t;  // 忽略第三个元素
 
tuple 的拼接:std::tuple_cat
 
auto t1 = std::make_tuple(1, "x");
auto t2 = std::make_tuple(3.14, false);
auto t3 = std::tuple_cat(t1, t2);  // 拼接 tuple
 
注意事项
std::get<index>(tuple)的 index 必须是 编译时常量。- 用类型访问元素(
std::get<T>)时类型必须唯一。 - tuple 不支持运行时下标访问(不像 vector)。
 - 不适用于极大量元素(模板编译速度会极慢)。
 std::apply是处理 tuple 和函数调用结合的利器(见下节)。
函数调用 + tuple 参数展开:std::apply
 
#include <tuple>void func(int a, double b, const std::string& c) {std::cout << a << ", " << b << ", " << c << "\n";
}int main() {auto t = std::make_tuple(1, 3.14, "hello"s);std::apply(func, t);  // 自动解包调用 func
}
 
实战示例:递归地对 tuple 中所有值加倍
template <std::size_t I = 0, typename... Ts>
void double_tuple(std::tuple<Ts...>& t) {if constexpr (I < sizeof...(Ts)) {std::get<I>(t) *= 2;double_tuple<I + 1>(t);}
}
 
适用于所有支持 *= 操作的类型。
小结
| 功能 | 工具 | 
|---|---|
| 构造 tuple | std::make_tuple, 构造函数 | 
| 访问元素 | std::get<index>, std::get<T> | 
| 获取信息 | std::tuple_size, std::tuple_element | 
| 遍历元素 | 模板递归 / std::apply + fold | 
| 类型安全调用 | std::apply(func, tuple) | 
| 拼接 | std::tuple_cat | 
| 解包 | C++17 结构化绑定、std::tie | 
| 忽略元素 | std::ignore |