为什么在c ++ 11中的lambda函数没有函数<>类型?

时间:2012-07-24 10:20:05

标签: c++ lambda functional-programming c++11

我正在玩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;,则可以接受它。类型。

所以我的问题是:

  1. 为什么lambda函数首先没有function<>类型?在小示例中,为什么addInt_l没有function<int (int,int)>作为类型,而不是具有不同的lambda类型?从函数式编程的角度来看,函数/函数对象和lambda之间有什么区别?

  2. 如果存在这两者必须不同的根本原因。我听说lambda可以转换为function<>,但它们是不同的。这是C ++ 11的一个设计问题/缺陷,一个实现问题,还是区分两者的好处?似乎仅addInt_l的类型签名提供了关于函数的参数和返回类型的足够信息。

  3. 有没有办法编写lambda,以便可以避免上面提到的显式类型转换?

  4. 提前致谢。

        //-- 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;
        }
    

2 个答案:

答案 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::functionstd::function为具有已知签名的可调用对象提供类型擦除。这基本上使得存储类型擦除的callables并编写virtual接口很有用。

答案 1 :(得分:5)

  1. 因为function<>使用type erasure。这允许将几种不同的类函数类型存储在function<>中,但会导致运行时间损失很小。类型擦除隐藏虚拟功能接口后面的实际类型(您的特定lambda)。
  2. 这有一个好处:C ++设计之一“公理”是永远不会增加开销,除非真的需要它。使用此设置,在使用类型推断时没有任何开销(使用auto或传递作为模板参数),但您仍然可以通过function<>完全灵活地与非模板代码进行交互。另请注意,function<>不是语言构造,而是可以使用简单语言功能实现的标准库的组件。
  3. 不,但您可以编写函数来获取函数的类型(语言构造)而不是function<>(库构造)的细节。当然,这使得实际写下返回类型变得更加困难,因为它不会直接为您提供参数类型。但是,使用一些元编程la Boost.FunctionTypes,您可以从传入的函数中推导出这些元素。在某些情况下,这是不可能的,例如对于具有模板化operator()的仿函数。