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

2021-12-23 00:00:00 lambda functional-programming c++ c++11

我正在尝试使用 c++11 功能特性.我觉得奇怪的一件事是 lambda 函数的类型实际上不是函数<> 类型.更重要的是,lambda 似乎并不能很好地与类型推断机制配合使用.

I am playing around with the c++11 functional features. One thing I find odd is that the type of a lambda function is actually NOT a function<> type. What's more, lambda's do not seem to play really well with the type-inferencing mechanism.

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

Attached is a small example in which I tested flipping the two arguments of a function for adding two integers. (The compiler I used was gcc 4.6.2 under MinGW.) In the example, the type for addInt_f has been explicitly defined using function<> while addInt_l is a lambda whose type is type-inferenced with auto.

当我编译代码时,flip 函数可以接受 addInt 的显式类型定义版本,但不能接受 lambda 版本,给出一个错误说,testCppBind.cpp:15:27: 错误:没有匹配的函数调用'flip(<lambda(int, int)>&)'

When I compiled the code, the flip function can accept the explicitly type-defined version of addInt but not the lambda version, giving an error saying that, testCppBind.cpp:15:27: error: no matching function for call to 'flip(<lambda(int, int)>&)'

接下来的几行表明,如果 lambda 版本(以及原始"版本)被显式转换为适当的函数<>类型,则可以接受它.

The next few lines show that the lambda version (as well as a 'raw' version) can be accepted if it's explicitly cast to the appropriate function<> type.

所以我的问题是:

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

  1. Why is it that a lambda function does not have a function<> type in the first place? In the small example, why does not addInt_l have function<int (int,int)> as the type instead of having a different, lambda type? From the perspective of functional programming, what's the difference between a function/functional object and a lambda?

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

If there is a fundamental reason that these two have to be different. I heard that lambda's can be converted to function<> but they are different. Is this a design issue/defect of C++11, an implementation issue or is there a benefit in distinguishing the two as the way it is? It seems that the type-signature of addInt_l alone has provided enough information about the parameter and return types of the function.

有没有办法编写 lambda 来避免上述显式类型转换?

Is there a way to write the lambda so that the above mentioned explicit type-casting can be avoided?

提前致谢.

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

推荐答案

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

std::function is a tool useful to store any kind of callable object regardless of its type. In order to do this it needs to employ some type erasure technique, and that involves some overhead.

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

Any callable can be implicitly converted to a std::function, and that's why it usually works seamlessly.

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

I'll repeat to make sure it becomes clear: std::function is not something just for lambdas or function pointers: it's for any kind of callable. That includes things like struct some_callable { void operator()() {} };, for example. That is a simple one, but it could be something like this instead:

struct some_polymorphic_callable {
    template <typename T>
    void operator()(T);
};

lambda 只是另一个可调用对象,类似于上面的 some_callable 对象的实例.它可以存储在 std::function 中,因为它是可调用的,但它没有 std::function 的类型擦除开销.

A lambda is just yet another callable object, similar to instances of the some_callable object above. It can be stored in a std::function because it's callable, but it doesn't have the type erasure overhead of std::function.

委员会计划在未来使 lambdas 成为多态的,即看起来像上面的 some_polymorphic_callable 的 lambdas.这样的 lambda 是哪种 std::function 类型?

And the committee plans to make lambdas polymorphic in the future, i.e., lambdas that look like some_polymorphic_callable above. Which std::function type would such a lambda be?

现在...模板参数推导,或隐式转换.选一个.这是 C++ 模板的规则.

Now... Template parameter deduction, or implicit conversions. Pick one. That's a rule of C++ templates.

要将 lambda 作为 std::function 参数传递,需要对其进行隐式转换.采用 std::function 参数意味着您选择隐式转换而不是类型推导.但是您的函数模板需要推导或显式提供签名.

To pass a lambda as a std::function argument, it needs to be implicitly converted. Taking a std::function argument means that you're choosing implicit conversions over type deduction. But your function template needs the signature to be deduced or provided explicitly.

解决方案?不要将您的调用者限制为 std::function.接受任何类型的可调用.

The solution? Don't restrict your callers to std::function. Accept any kind of callable.

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 为具有已知签名的可调用对象提供类型擦除.这基本上使得存储类型擦除的可调用对象和编写虚拟接口很有用.


You may now be thinking why do we need std::function then. std::function provides type erasure for callables with a known signature. That essentially makes it useful to store type-erased callables and to write virtual interfaces.

相关文章