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

tin*_*lyx 17 c++ lambda functional-programming c++11

我正在玩c ++ 11的功能.我发现奇怪的一件事是lambda函数的类型实际上不是函数<>类型.更重要的是,lambda似乎不能很好地使用类型推理机制.

附件是一个小例子,我在其中测试了翻转函数的两个参数以添加两个整数.(我使用的编译器是MinGW下的gcc 4.6.2.)在示例中,类型for addInt_f已使用函数<>显式定义,而addInt_l类型为类型推理的lambda auto.

当我编译的代码,该flip功能可以接受明确类型定义addInt的版本,但不是拉姆达版本,给了一个错误说, testCppBind.cpp:15:27: error: no matching function for call to 'flip(<lambda(int, int)>&)'

接下来的几行显示lambda版本(以及'raw'版本)如果显式转换为适当的函数<>类型,则可以接受.

所以我的问题是:

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

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

  3. 有没有办法编写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;
    }
Run Code Online (Sandbox Code Playgroud)

R. *_*des 41

std::function是一种用于存储任何类型的可调用对象的工具,无论其类型如何.为了做到这一点,它需要采用一些类型的擦除技术,这涉及一些开销.

任何可调用的都可以隐式转换为a std::function,这就是它通常无缝工作的原因.

我将重复以确保它变得清晰:std::function不仅仅是lambdas或函数指针的东西:它适用于任何类型的可调用.这包括struct some_callable { void operator()() {} };例如.这是一个简单的,但它可能是这样的:

struct some_polymorphic_callable {
    template <typename T>
    void operator()(T);
};
Run Code Online (Sandbox Code Playgroud)

lambda只是另一个可调用对象,类似于some_callable上面对象的实例.它可以存储在a中,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); }
Run Code Online (Sandbox Code Playgroud)

你现在可能在想我们为什么需要std::function呢.std::function为具有已知签名的可调用字符提供类型擦除.这基本上使得存储类型擦除的callables和编写virtual接口很有用.


ltj*_*jax 5

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