C++20下对std::tuple各类型应用模板lambda的实现与标准库方法疑问
C++20下对std::tuple各类型应用模板lambda的实现与标准库方法疑问
嘿,你这个需求简直是C++20模板lambda推出后最顺理成章的使用场景之一,完全懂你为啥会觉得这应该是标准操作!先直接回答你的两个问题:
1. 有没有C++20标准库已有的方法?
你没漏任何关键内容——C++20标准库确实没有直接支持这个特性。正如你猜测的,std::apply在C23才获得了对模板化可调用对象的适配能力:在C23里,当你给std::apply传一个模板lambda(或其他模板可调用对象),它会自动对tuple的每个元素的类型实例化这个模板调用。但在C++20,std::apply的设计核心是处理tuple的值,而非类型参数化的调用,所以你得自己绕个弯子实现。
2. 更优雅的C++20实现方案?
你的现有实现虽然能工作,但有个隐藏的局限性:它依赖tuple里的每个类型都支持默认构造。如果你的tuple里有个类型比如struct NoDefault { NoDefault(int) {} };,那static constexpr Tuple defaultTuple{}这行就会编译失败,因为没法默认构造这个类型。
给你一个更通用、更简洁的版本,不需要构造任何tuple实例,直接通过类型索引遍历tuple的每个类型:
#include <tuple> #include <utility> #include <iostream> #include <typeinfo> template <typename T> void logType() { std::cout << "Type: " << typeid(T).name() << std::endl; } template <typename Tuple, typename Func> void tapply(Func&& func) { // 用索引序列遍历tuple的每个类型位置 [&func]<std::size_t... Is>(std::index_sequence<Is...>) { // 对每个索引对应的tuple类型,实例化模板lambda (func.template operator()<std::tuple_element_t<Is, Tuple>>(), ...); }(std::make_index_sequence<std::tuple_size_v<Tuple>>{}); } // 测试用的无默认构造类型 struct NoDefault { NoDefault(int) {} }; int main() { using MyTypes = std::tuple<float, int, std::string, NoDefault>; // 即使NoDefault没有默认构造,这个调用也能正常工作 tapply<MyTypes>([]<typename T>() { logType<T>(); }); }
这个版本的优势:
- 不依赖元素类型的默认构造,适用性更广;
- 逻辑更直接:直接通过tuple的类型索引获取每个元素类型,不需要借助
std::apply和临时tuple对象绕圈; - 代码更紧凑,去掉了不必要的
std::decay_t和临时tuple构造。
如果需要tapply支持返回值(比如收集每个func<T>()的返回值成一个tuple),还可以扩展成:
template <typename Tuple, typename Func> auto tapply(Func&& func) { return [&func]<std::size_t... Is>(std::index_sequence<Is...>) { return std::make_tuple(func.template operator()<std::tuple_element_t<Is, Tuple>>()...); }(std::make_index_sequence<std::tuple_size_v<Tuple>>{}); }
总的来说,C20确实把模板lambda这个好用的工具给了我们,但配套的“类型tuple遍历”工具没跟上,直到C23才补全了std::apply的这个能力。你的原始实现是C++20下的可行方案,但优化后的索引序列版本更健壮优雅~
内容来源于stack exchange
相关产品推荐
相关产品推荐

