如何在MSVC的C++17环境下按索引编译期遍历std::tuple
C++17跨平台遍历std::tuple并获取索引的实现方案
原代码编译失败的核心原因:你使用的lambda模板形参<size_t ...d>属于C20引入的特性,GCC/Clang在C17模式下将其作为非标准扩展支持,而MSVC的C++17标准模式不支持该扩展,因此无法编译。
以下是两种符合C++17标准、全平台兼容的优雅实现:
方案1:通用辅助函数(支持编译期索引)
该方案实现了可复用的遍历工具,拿到的索引是编译期常量,可直接作为模板参数使用,适配所有场景:
工具函数定义
#include <tuple> #include <utility> #include <cstddef> #include <type_traits> template <typename Tuple, typename Func, size_t... Idx> void tuple_for_each_index_impl(Tuple&& t, Func&& f, std::index_sequence<Idx...>) { (f(std::integral_constant<size_t, Idx>{}, std::get<Idx>(std::forward<Tuple>(t))), ...); } template <typename Tuple, typename Func> void tuple_for_each_with_index(Tuple&& t, Func&& f) { constexpr size_t N = std::tuple_size_v<std::remove_reference_t<Tuple>>; tuple_for_each_index_impl(std::forward<Tuple>(t), std::forward<Func>(f), std::make_index_sequence<N>{}); }
使用示例
#include <array> struct A { int val() { return 1; } }; struct B { int v; B(int v): v(v) {}; int val() { return v * 2; } }; int main() { auto arr = std::array<int,3>{}; auto t = std::tuple<A,A,B>{A{}, A{}, B{7}}; tuple_for_each_with_index(t, [&](auto idx, auto& elem) { // idx 可隐式转换为size_t,也可直接作为模板参数使用 arr[idx] = elem.val(); }); return 0; }
方案2:std::apply简化写法(仅需运行期索引时使用)
如果你的逻辑不需要把索引作为模板参数使用,可直接用更简洁的局部写法,编译器优化后性能和编译期索引完全一致:
#include <tuple> #include <array> struct A { int val() { return 1; } }; struct B { int v; B(int v): v(v) {}; int val() { return v * 2; } }; int main() { auto arr = std::array<int,3>{}; auto t = std::tuple<A,A,B>{A{}, A{}, B{7}}; std::apply([&](auto&&... elems) { size_t idx = 0; ((arr[idx++] = elems.val()), ...); }, t); return 0; }
两种方案均完全兼容C++17标准,可在MSVC、GCC、Clang三大编译器下正常编译运行,运行效率和你最初的写法没有任何差异。如果tuple元素均继承自同一基类,可在lambda中直接转换为基类引用做统一处理。
内容的提问来源于stack exchange,提问作者Kyle Beyer
相关产品推荐
相关产品推荐

