From 772478d9184314f6e9faa6ee4c308a8d5e60f2eb Mon Sep 17 00:00:00 2001 From: Changkun Ou Date: Sun, 7 Jun 2026 16:17:04 +0200 Subject: [PATCH] book: add idiomatic std::tuple traversal (index_sequence / C++20 lambda) The existing tuple traversal builds on the variant-based runtime indexing and a for loop, which is roundabout for simply applying an operation to each element. Add the idiomatic compile-time approaches: C++17 fold + std::index_sequence, and the C++20 templated-lambda form. Both verified to compile and produce correct output. Fixes #294 --- book/en-us/04-containers.md | 31 +++++++++++++++++++++++++++++++ book/zh-cn/04-containers.md | 31 +++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/book/en-us/04-containers.md b/book/en-us/04-containers.md index 36acbb3b..15d9f7c7 100644 --- a/book/en-us/04-containers.md +++ b/book/en-us/04-containers.md @@ -295,6 +295,37 @@ for(int i = 0; i != tuple_len(new_tuple); ++i) std::cout << tuple_index(new_tuple, i) << std::endl; ``` +That said, traversing a tuple by "first implementing runtime indexing, then indexing element by element" works but is rather roundabout. If all you want is to apply the same operation to every element, the more direct and idiomatic way is to expand the indices at compile time with `std::index_sequence` (introduced in C++14). In C++17, it can be combined with a fold expression: + +```cpp +template +void iterate_impl(Func&& f, Tuple&& tpl, std::index_sequence) { + (f(std::get(std::forward(tpl))), ...); +} +template +void iterate_tuple(Func&& f, Tuple&& tpl) { + iterate_impl(std::forward(f), std::forward(tpl), + std::make_index_sequence>>{}); +} +``` + +In C++20, we can further drop the helper function by using a lambda that allows explicitly written template parameters: + +```cpp +template +void iterate_tuple(Func f, const std::tuple& tpl) { + [&](std::index_sequence) { + (f(std::get(tpl)), ...); + }(std::make_index_sequence()); +} +``` + +The call site is then very straightforward, and no runtime indexing is needed beforehand: + +```cpp +iterate_tuple([](const auto& v) { std::cout << v << ' '; }, new_tuple); +``` + ## Conclusion This chapter briefly introduces the new containers in modern C++. Their usage is similar to that of the existing containers in C++. It is relatively simple, and you can choose the containers you need to use according to the actual scene, to get better performance. diff --git a/book/zh-cn/04-containers.md b/book/zh-cn/04-containers.md index b884ffda..5657cd51 100644 --- a/book/zh-cn/04-containers.md +++ b/book/zh-cn/04-containers.md @@ -295,6 +295,37 @@ for(int i = 0; i != tuple_len(new_tuple); ++i) std::cout << tuple_index(new_tuple, i) << std::endl; ``` +不过,上面这种「先实现运行期索引、再逐个索引」的遍历方式虽然可行,却相当迂回。如果只是想对元组的每个元素施加同一个操作,更直接、惯用的做法是借助 `std::index_sequence`(C++14 引入)在编译期展开下标。在 C++17 中可以配合折叠表达式写成: + +```cpp +template +void iterate_impl(Func&& f, Tuple&& tpl, std::index_sequence) { + (f(std::get(std::forward(tpl))), ...); +} +template +void iterate_tuple(Func&& f, Tuple&& tpl) { + iterate_impl(std::forward(f), std::forward(tpl), + std::make_index_sequence>>{}); +} +``` + +到了 C++20,还可以利用允许显式书写模板参数的 Lambda,把辅助函数也一并省去: + +```cpp +template +void iterate_tuple(Func f, const std::tuple& tpl) { + [&](std::index_sequence) { + (f(std::get(tpl)), ...); + }(std::make_index_sequence()); +} +``` + +这样调用就非常直观了,而且无需事先实现运行期索引: + +```cpp +iterate_tuple([](const auto& v) { std::cout << v << ' '; }, new_tuple); +``` + ## 总结 本章简单介绍了现代 C++ 中新增的容器,它们的用法和传统 C++ 中已有的容器类似,相对简单,可以根据实际场景丰富的选择需要使用的容器,从而获得更好的性能。