我正在尝试用C ++学习lambdas,但偶然发现了一些我无法理解的内容。
这是代码:
#include <iostream>
typedef double Func(double);
double applyFunc(Func f, double x)
{
return f(x);
}
int main()
{
std::cout << applyFunc([](double x) {return x + 1;}, 3) << std::endl;
}
现在这很好用(打印“4”),即使用的lambda表达式的类型正好是double (*)(double)
。
但是如果我在lambda表达式中添加闭包,比如:
int main()
{
int n = 5;
std::cout << applyFunc([n](double x) {return x + n;}, 3) << std::endl;
}
然后我从编译器得到错误:
In function ‘int main()’:
error: cannot convert ‘main()::__lambda0’ to ‘double (*)(double)’ for argument ‘1’ to ‘double applyFunc(double (*)(double), double)’
3) << std::endl;
^
我不明白为什么会这样。我的意思是,从applyFunc()
的角度来看,它仍然收到一个指向函数的指针,该函数采用double
参数并返回double
,并且它不知道我们使用变量'n'从上下文来看,所以lambda表达式的类型应该是相同的,就像在第一个例子中一样,对吗?
我非常感谢您的帮助,谢谢您!
答案 0 :(得分:4)
只有在没有捕获的情况下,lambda才可转换为函数指针,我们可以通过转到草案标准部分5.1.2
Lambda表达式来看到这一点(强调我的):
没有lambda-capture 的lambda表达式的闭包类型有一个 公共非虚拟非显式const 转换函数到指针 使用具有与闭包相同的参数和返回类型 type的函数调用运算符。此转换返回的值 function应该是一个函数的地址,当被调用时,它具有 与调用闭包类型的函数调用操作符的效果相同。
这是一种不依赖于此转换的替代解决方案:
template <typename Callable>
double applyFunc(Callable f, double x)
{
return f(x);
}
更新
如果您对使用std::function
和模板之间的区别感兴趣,那么您应该阅读std::function vs template。那里有很多好的答案,还有很多值得思考的东西。
答案 1 :(得分:2)
每个lambda表达式返回一个具有不同类型的对象。如果lambda表达式不捕获任何变量,则可以将其转换为适当签名的函数指针类型(即,返回类型和参数需要与lambda表达式的那些一致)。当捕获变量时,创建的实体不能由普通函数表示。相反,lambda表达式产生一个带有函数调用操作符的类类型的对象。也就是说,你的第二个代码使用一个lambda表达式,产生一个类的对象,大致相当于这个:
class lambda {
int n;
public:
lambda(int n): n(n) {}
double operator()(double x) const { return x + n; }
};