使用Lambda函数指针适配模板时的编译错误问题咨询
嗨,这个问题我刚学C++11的时候也踩过一模一样的坑!核心原因其实是lambda的本质和模板参数推导的规则在起作用,咱们慢慢掰扯清楚:
首先,lambda不是函数指针,即使它看起来像
你写的mul3是一个无捕获lambda,虽然无捕获lambda确实可以隐式转换为对应的函数指针,但auto推导的时候,会老老实实地推导出lambda的真实类型——也就是编译器在编译时生成的一个匿名函数对象(闭包类型),而不是int(*)(int)这种函数指针类型。
简单说:auto mul3 = [...]之后,mul3的类型是一个编译器生成的类类型,不是函数指针。
然后,模板参数推导不“认”隐式转换
你的map函数是一个模板函数,第二个参数要求是T(*func)(T)——也就是和T对应的函数指针。当你传递mul2的时候,mul2本身就是int(*)(int)类型,和模板推导出来的T=int完全匹配,所以运行毫无问题。
但传递mul3的时候就不一样了:模板参数推导是严格按照实参的原始类型来匹配的,它不会主动去做隐式转换(比如把闭包类型转成函数指针)。所以编译器看到mul3的类型是闭包类型,而形参要求是函数指针,类型不匹配,自然就报错了。
(补充一句:如果你直接把lambda写到map的参数里,比如map(v, [](int v){return v*3;});,这时候编译器会自动把lambda隐式转换成函数指针,因为这时候实参是一个临时的lambda对象,编译器会触发这个隐式转换来匹配形参。但赋值给auto变量后,变量类型固定为闭包类型,就不会自动转了。)
怎么解决这个问题?
有几个常用的方案,按推荐程度排序:
1. 让map函数支持任意可调用对象(最推荐)
把map的模板改成接受任意可调用类型,而不是局限于函数指针,这样不管是lambda、函数对象还是普通函数都能支持,通用性更强:
#include <iostream> #include <vector> template <class T, class Func> void map(std::vector<T>& values, Func func) { // 注意这里原来写的是int&,要改成T&,否则非int类型的vector会报错 for (T &value : values) { value = func(value); } } int mul2(int x) { return 2*x; } auto mul3 = [](int value) { return value * 3; }; int main() { std::vector<int> v = { 1,2,3,4,5 }; map(v, mul3); for (auto value : v) { std::cout << value << std::endl; } }
2. 显式指定mul3的类型为函数指针
如果你非要用函数指针,可以直接把mul3声明成函数指针类型,而不是用auto:
int(*mul3)(int) = [](int value) { return value * 3; };
这样mul3的类型就是int(*)(int),传递给map就完全匹配了。
3. 调用时显式转换
在调用map的时候,把mul3强制转换成函数指针类型:
map(v, static_cast<int(*)(int)>(mul3));
这种方式比较繁琐,不推荐,但应急的时候能用。
最后再提一句你代码里的小细节:原来的for (int &value : values)要改成T& value,不然如果你的vector存的是其他类型(比如double),这里就会出现类型不匹配的错误。
内容的提问来源于stack exchange,提问作者Norhther

