为什么C ++ 11不会将lambdas隐式转换为std :: function对象? [英] Why doesn't C++11 implicitly convert lambdas to std::function objects?

查看:198
本文介绍了为什么C ++ 11不会将lambdas隐式转换为std :: function对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我实现了一个通用的事件发射器类,它允许代码注册回调,并发出带有参数的事件。我使用Boost.Any类型擦除来存储回调,以便他们可以有任意参数签名。

I implemented a generic event emitter class which allows code to register callbacks, and emit events with arguments. I used Boost.Any type erasure to store the callbacks so they can have arbitrary parameter signatures.

这一切都行之有效,但由于某种原因,传递的lambda必须先变成 std :: function 对象。为什么编译器不推断lambda是函数类型?是因为我使用可变参数模板的方式?

It all works, but for some reason, lambdas being passed in must first be turned into std::function objects. Why doesn't the compiler infer that the lambda is the function type? Is it because of the way I use variadic templates?

我使用Clang(版本字符串: Apple LLVM版本5.0(clang-500.2.79) (基于LLVM 3.3svn))。

I use Clang (version string: Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)).

代码:

#include <functional>
#include <iostream>
#include <map>
#include <string>
#include <vector>

#include <boost/any.hpp>


using std::cout;
using std::endl;
using std::function;
using std::map;
using std::string;
using std::vector;


class emitter {

   public:

      template <typename... Args>
      void on(string const& event_type, function<void (Args...)> const& f) {
         _listeners[event_type].push_back(f);
      }

      template <typename... Args>
      void emit(string const& event_type, Args... args) {
         auto listeners = _listeners.find(event_type);
         for (auto l : listeners->second) {
            auto lf = boost::any_cast<function<void (Args...)>>(l);
            lf(args...);
         }
      }

   private:

      map<string, vector<boost::any>> _listeners;

};


int main(int argc, char** argv) {

   emitter e;

   int capture = 6;

   // Not sure why Clang (at least) can't deduce the type of the lambda. I don't
   // think the explicit function<...> business should be necessary.
   e.on("my event",
        function<void ()>( // <--- why is this necessary?
           [&] () {
              cout << "my event occurred " << capture << endl;
           }));
   e.on("my event 2",
        function<void (int)>(
           [&] (int x) {
              cout << "my event 2 occurred: " << x << endl;
           }));
   e.on("my event 3",
        function<void (double)>(
           [&] (double x) {
              cout << "my event 3 occurred: " << x << endl;
           }));
   e.on("my event 4",
        function<void (int, double)>(
           [&] (int x, double y) {
              cout << "my event 4 occurred: " << x << " " << y << endl;
           }));

   e.emit("my event");
   e.emit("my event 2", 1);
   e.emit("my event 3", 3.14159);
   e.emit("my event 4", 10, 3.14159);

   return EXIT_SUCCESS;
}


推荐答案

lambda不是 std :: function std :: function 不是lambda。

A lambda is not a std::function, and std::function is not a lambda.

lambda是一种语法糖,用于创建一个如下所示的匿名类:

A lambda is syntactic sugar to create an anonymous class that looks like this:

struct my_lambda {
private:
  int captured_int;
  double captured_double;
  char& referenced_char;
public:
  int operator()( float passed_float ) const {
    // code
  }
};
int captured_int = 7;
double captured_double = 3.14;
char referenced_char = 'a';
my_lambda closure {captured_int, captured_double, referenced_char};
closure( 2.7f );

int captured_int = 7;
double captured_double = 3.14;
char referenced_char = 'a';
auto closure = [=,&referenced_char](float passed_float)->int {
  // code
};
closure(2.7);

是一些不可命名的类型。

with the type name of the my_lambda actually being some unnameable type.

一个 std :: function 是一个完全不同的东西。它是一个具有特定签名的实现 operator()的对象,并将一个智能值语义指针存储到一个包含复制/移动/调用操作的抽象接口。它有一个模板 d构造函数,可以采用支持复制/移动/ operator() ,生成实现抽象内部接口的具体自定义类,并将其存储在上述内部值 - 语义智能指针中。

A std::function is a completely different thing. It is an object that does implement operator() with a particular signature, and stores a smart value-semantics pointer to an abstract interface that covers copy/move/invoke operations. It has a templated constructor that can take any type that supports copy/move/operator() with a compatible signature, generates a concrete custom class that implement the abstract internal interface, and stores it in the above mentioned internal value-semantics smart pointer.

然后将操作从自身转发为

It then forwards operations from itself as a value-type to the abstract internal pointer, including perfect forwarding to the invocation method.

在这种情况下,您可以在一个 std :: function ,就像你可以存储一个函数指针。

As it happens, you can store a lambda in a std::function, just like you can store a function pointer.

但是有很多不同的 std :: function 可以存储一个给定的lambda - 任何东西,其中类型可以转换为和从参数工作,

But there are a whole myriad of different std::function that could store a given lambda -- anything where the types are convertible to and from the arguments works, and in fact works equally well, as far as the std::function is concerned.

在<$ c $中键入C ++中的扣除c> template s不能在你可以转换为级别工作 - 它是模式匹配,纯粹和简单。由于lambda是与任何 std :: function 无关的类型,因此不能从 std :: function

Type deduction in C++ in templates does not work at the level "can you convert into" -- it is pattern matching, pure and simple. As the lambda is a type unrelated to any std::function, no std::function type can be deduced from it.

如果C ++试图在一般情况下这样做,它必须反转一个图灵完成过程来确定什么(如果有的话)

If C++ tried to do that in the general case, it would have to invert a Turing-complete process to determine what (if any) set of types could be passed to the template in order to generate a conversion-compatible instance.

在理论上,我们可以添加运算符,并将其传递给模板从到语言中推导模板参数,其中给定的模板的实现者可以编写采用某种任意类型的代码,并且他们试图从这种类型, template 参数应该用于实例。但C ++没有这个。

In theory, we could add "operator deduce template arguments from" to the language, where the implementers of a given template can write code that takes some arbitrary type, and they attempt to tease out "from this type, what template parameters should be used for an instance". But C++ does not have this.

这篇关于为什么C ++ 11不会将lambdas隐式转换为std :: function对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆