第一个错误:std::function
是一类无关的任何拉姆达。
一个lambda是一个匿名类型,具有operator()
和一些其他已知属性。
std::function<R(Args...)>
是用于复制构造,销毁和调用Args...
并返回R
的类型擦除类。它可以用lambda构造,但不是相关的类型。
由于您无法命名lambda类型,因此使用std::function
进行存储是很常见的。然而,拉姆达不是std::function
。从它们的类型擦除和多态性开始,几乎不可避免地产生开销:lambda缺少任何多态性,这使得编译器很容易理解调用时的作用。
在你的情况下,你有两个lambda。
你的第一拉姆达是:
[](auto&& f0,auto&& a0){return f0(f0,a0);}
这看起来像Y型组合子的形式,或变体,用来帮助与递归。 operator()
在这种情况下有签名:
template<class F0, class A0>
auto operator()(F0&&,A0&&)const
-> std::result_of_t<F0&(F0&,A0&)>
粗略。
更有用的版本(在我看来)是:
[](auto&& f0){
return [f0=std::forward<decltype(f0)>(f0)]
(auto&&...args) {
return f0(f0, std::forward<decltype(args)>(args)...);
};
}
这需要一个f0
,将其存储,并与任何参数传递f0
首先调用它。这可以让你将递归绑定在“视线之外”。使内拉姆达mutable
是可选的(取决于如果你想在一个const
上下文来调用)总之
,接下来的拉姆达:
[](auto& f,auto&& a)->int{ return (a>1) ? f(f,a-1)*a : 1; }
具有的operator()
签名:
template<class F, class A>
auto operator()(F&,A&&)const
-> int
你然后将第二个lambda的实例传递给第一个,加上一个参数,然后计算n!
。
由template
运营商()
推导出的类型不依赖于参数本身演绎的类型,所以没有无限类型演绎问题。内部拉姆达的返回类型被硬编码为int
,因此您不必推断()
递归返回以知道它返回int
。
但是,如果您想将第一个lambda存储在std::function
中,您将会感到失望。 std::function
不能擦除template operator()
:它只能擦除固定的签名,而template
成员是方法的工厂,而不是方法本身。
但是,请记住我上面的y组合的更好的版本?
致电您的第一个lambda g
,您的第二个h
和我的lambda y
和lambda my lambda返回z
。
然后g(h,x)
= y(h)(x)
- 和y(h)
可以存储在std::function<int(int)>
没有问题。我们隐藏了基本上需要递归类型签名的递归部分,其中std::function
不支持。剩下的东西虽然有template operator()
,但可以绑定到简单的签名。
注意,你可以写std::function
支持递归式签名,就像std::function< std::vector<SELF_TYPE>(int) >
。你可以看到boost::variant
如何与递归变体一起工作。
由于您明确提到了lambda的返回类型,因此编译器无需查看递归调用'f(f,a-1)'的路径。如果你删除了' - > int',你会遇到问题。 – 0x499602D2 2015-02-08 02:24:23