我正在玩c ++ 11功能。我发现奇怪的一件事是lambda函数的类型实际上不是函数<>类型。更重要的是,lambda似乎不能很好地使用类型推理机制。
附件是一个小例子,我测试了翻转函数的两个参数以添加两个整数。 (我使用的编译器是MinGW下的gcc 4.6.2。)在示例中,addInt_f
的类型已使用函数<>显式定义。而addInt_l
是一个lambda,其类型与auto
类型相关。
当我编译代码时,flip
函数可以接受显式类型定义的addInt版本但不接受lambda版本,给出错误说明,
testCppBind.cpp:15:27: error: no matching function for call to 'flip(<lambda(int, int)>&)'
接下来的几行显示,如果lambda版本(以及'raw'版本)明确地转换为适当的函数&lt;&gt;,则可以接受它。类型。
所以我的问题是:
为什么lambda函数首先没有function<>
类型?在小示例中,为什么addInt_l
没有function<int (int,int)>
作为类型,而不是具有不同的lambda
类型?从函数式编程的角度来看,函数/函数对象和lambda之间有什么区别?
如果存在这两者必须不同的根本原因。我听说lambda可以转换为function<>
,但它们是不同的。这是C ++ 11的一个设计问题/缺陷,一个实现问题,还是区分两者的好处?似乎仅addInt_l
的类型签名提供了关于函数的参数和返回类型的足够信息。
有没有办法编写lambda,以便可以避免上面提到的显式类型转换?
提前致谢。
//-- testCppBind.cpp --
#include <functional>
using namespace std;
using namespace std::placeholders;
template <typename T1,typename T2, typename T3>
function<T3 (T2, T1)> flip(function<T3 (T1, T2)> f) { return bind(f,_2,_1);}
function<int (int,int)> addInt_f = [](int a,int b) -> int { return a + b;};
auto addInt_l = [](int a,int b) -> int { return a + b;};
int addInt0(int a, int b) { return a+b;}
int main() {
auto ff = flip(addInt_f); //ok
auto ff1 = flip(addInt_l); //not ok
auto ff2 = flip((function<int (int,int)>)addInt_l); //ok
auto ff3 = flip((function<int (int,int)>)addInt0); //ok
return 0;
}
答案 0 :(得分:41)
std::function
是一个对存储任何类型的可调用对象都有用的工具,无论其类型如何。为了做到这一点,它需要采用一些类型的擦除技术,这涉及一些开销。
任何可调用的都可以隐式转换为std::function
,这就是它通常无缝运行的原因。
我将重复以确保它变得清晰:std::function
不仅仅适用于lambda或函数指针:它适用于任何类型的可调用。其中包括struct some_callable { void operator()() {} };
之类的内容。这是一个简单的,但它可能是这样的:
struct some_polymorphic_callable {
template <typename T>
void operator()(T);
};
lambda只是另一个可调用对象,类似于上面some_callable
对象的实例。它可以存储在std::function
中,因为它可以调用,但它没有std::function
的类型擦除开销。
该委员会计划在未来使lambdas具有多态性,即上面看起来像some_polymorphic_callable
的lambdas。哪个std::function
类型会是这样的lambda?
现在...模板参数扣除或隐式转换。选一个。这是C ++模板的规则。
要将lambda作为std::function
参数传递,需要隐式转换它。采用std::function
参数意味着您选择隐式转换而不是类型推导。但是您的功能模板需要明确推断或提供签名。
解决方案?请勿将来电者限制为std::function
。接受任何类型的可调用。
template <typename Fun>
auto flip(Fun&& f) -> decltype(std::bind(std::forward<Fun>(f),_2,_1))
{ return std::bind(std::forward<Fun>(f),_2,_1); }
您现在可能正在考虑为什么我们需要std::function
。 std::function
为具有已知签名的可调用对象提供类型擦除。这基本上使得存储类型擦除的callables并编写virtual
接口很有用。
答案 1 :(得分:5)
function<>
使用type erasure。这允许将几种不同的类函数类型存储在function<>
中,但会导致运行时间损失很小。类型擦除隐藏虚拟功能接口后面的实际类型(您的特定lambda)。auto
或传递作为模板参数),但您仍然可以通过function<>
完全灵活地与非模板代码进行交互。另请注意,function<>
不是语言构造,而是可以使用简单语言功能实现的标准库的组件。function<>
(库构造)的细节。当然,这使得实际写下返回类型变得更加困难,因为它不会直接为您提供参数类型。但是,使用一些元编程la Boost.FunctionTypes,您可以从传入的函数中推导出这些元素。在某些情况下,这是不可能的,例如对于具有模板化operator()
的仿函数。