为什么std::apply传入函数模板失败,但传入C++20显式模板lambda却成功?
核心原因解析
为什么普通函数模板无法直接传入std::apply
函数模板并不是一个可直接使用、有确定类型的实体,它只是用于生成具体函数的蓝图。你直接书写add_generic这个模板名时,编译器无法自行确定应该将它实例化为哪个具体类型的函数,而std::apply的第一个参数要求是有确定类型的可调用对象,因此没有指定模板参数的函数模板无法完成类型推导,编译失败。
示例代码:
template<typename T> T add_generic(T first, T second) { return first + second; } // 编译错误:无法推导add_generic的实例化类型 std::apply(add_generic, std::make_pair(2.0f, 3.0f));
如果给普通函数模板显式指定实例化参数,std::apply(add_generic<double>, std::make_pair(2.0,3.0))就可以正常编译,本质就是此时传递的是已经实例化的、有确定类型的函数。
为什么C++20带显式模板参数的lambda可以正常工作
lambda表达式本质上是编译器生成的闭包类型的实例,不管它的调用运算符是不是模板,这个闭包实例本身的类型是完全确定的,不需要额外推导。
你给出的C++20 lambda:
[]<typename T>(T first,T second){ return first+second; }
编译器会为它生成一个类似如下结构的闭包类型:
struct __anonymous_closure { template<typename T> T operator()(T first, T second) const { return first + second; } };
当你把这个lambda传给std::apply时,std::apply首先要推导的可调用对象类型就是这个__anonymous_closure,类型完全确定,没有任何歧义。后续std::apply内部会把tuple解包为两个double类型的参数,调用闭包的operator()时,自然可以推导出模板参数T为double,完成实例化,整个过程没有障碍。
普通函数模板和带模板参数lambda的核心差异
- 普通函数模板本身没有确定类型,只有在显式指定模板参数、或者能通过调用上下文推导出模板参数完成实例化后,才能得到可传递的具体函数实体。在
std::apply的调用场景下,没有足够的上下文直接推导函数模板的实例化类型,因此失败。 - 带显式模板参数的lambda(包括泛型lambda)本身是有确定类型的闭包实例,可直接作为参数传递给
std::apply,它的模板化调用运算符的推导是在std::apply内部完成调用时进行的,此时已经有解包后的实参,推导可以顺利完成。
内容的提问来源于stack exchange,提问作者Saeed Masoomi
相关产品推荐
相关产品推荐

