使用std::apply遍历std::tuple:lambda可用但自由函数报错
std::apply调用tuple成员函数:Lambda与自由函数的差异分析
先看一段能复现问题的代码:
#include <tuple> #include <iostream> struct A { void print() const { std::cout << "A\n"; } }; struct B { void print() const { std::cout << "B\n"; } }; struct C { void print() const { std::cout << "C\n"; } }; // 自定义自由函数模板 template<typename... Ts> void printAll2(const Ts&... args) { (args.print(), ...); } int main() { auto t = std::make_tuple(A{}, B{}, C{}); // Lambda版本:正常编译运行 std::apply([](const auto&... args) { (args.print(), ...); }, t); // 自由函数版本:编译报错,提示无法推断_Fn模板参数 std::apply(printAll2, t); }
核心差异在于模板参数推导的逻辑
Lambda是自带泛型调用运算符的闭包
当你写泛型Lambda(用auto参数)时,编译器会自动生成一个闭包类型,这个类型里包含一个模板化的operator()。std::apply调用这个闭包时,会把tuple的所有元素拆成参数传给operator(),此时编译器能直接根据传入的元素类型,推导operator()的模板参数,整个过程顺畅自然。自由函数模板无法被反向推导
printAll2是一个函数模板,它本身不是一个具体的函数——只有当编译器知道Ts...的具体类型时,才会实例化出对应的函数。而std::apply的模板参数推导规则,没办法从tuple的元素类型反向推导出printAll2需要的Ts...参数。简单说就是:std::apply不知道该把printAll2实例化成针对A,B,C的版本,因为模板函数名字本身不能作为可调用实体被直接推导。
怎么让自由函数版本正常工作?
有两种简单的解决方式:
- 显式指定模板参数:直接告诉编译器要实例化哪个版本的
printAll2std::apply(printAll2<A, B, C>, t); - 把自由函数改成仿结构体:模仿Lambda的闭包逻辑,用带模板
operator()的结构体作为可调用对象struct PrintAll { template<typename... Ts> void operator()(const Ts&... args) const { (args.print(), ...); } }; // 调用时直接传结构体实例 std::apply(PrintAll{}, t);
内容的提问来源于stack exchange,提问作者das_blob
相关产品推荐
相关产品推荐

