为什么C++ 11没有隐式地将lambda转换为std :: function对象?

 时尚潮_流早覀报_326 发布于 2023-01-29 10:30

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

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

我使用Clang(版本字符串:) Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn).

码:

#include 
#include 
#include 
#include 
#include 

#include 


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


class emitter {

   public:

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

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

   private:

      map> _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( // <--- why is this necessary?
           [&] () {
              cout << "my event occurred " << capture << endl;
           }));
   e.on("my event 2",
        function(
           [&] (int x) {
              cout << "my event 2 occurred: " << x << endl;
           }));
   e.on("my event 3",
        function(
           [&] (double x) {
              cout << "my event 3 occurred: " << x << endl;
           }));
   e.on("my event 4",
        function(
           [&] (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;
}

Yakk - Adam .. 14

lambda不是a std::function,std::function也不是lambda.

lambda是一个语法糖来创建一个如下所示的匿名类:

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);

my_lambda实际上是一些不可命名的类型的类型名称.

A std::function是完全不同的东西.它是一个operator()使用特定签名实现的对象,并将智能值语义指针存储到覆盖复制/移动/调用操作的抽象接口.它有一个template构造函数,可以采用任何支持copy/move/operator()with兼容签名的类型,生成一个实现抽象内部接口的具体自定义类,并将其存储在上面提到的内部值语义智能指针中.

然后它将操作从自身转发为值类型到抽象内部指针,包括完美转发到调用方法.

实际上,您可以将lambda存储在a中std::function,就像存储函数指针一样.

但是有一大堆不同的东西std::function可以存储一个给定的lambda - 任何类型可以转换为参数和从参数工作的东西,并且实际上同样有效,就所std::function涉及的而言.

在C++中template的类型推导在s中不起作用"你可以转换成" - 它是模式匹配,纯粹和简单.由于lambda是一种与any无关的类型std::function,因此std::function不能从中推导出任何类型.

如果C++试图在一般情况下这样做,则必须反转图灵完备过程以确定可以将哪些(如果有的话)类型集传递给它template以生成与转换兼容的实例.

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

2 个回答
  • 编译器不会推断任何内容,因为编译器实现了C++语言,并且语言的模板参数推导规则不允许以您希望的方式进行推导.

    这是一个代表您情况的简单示例:

    template <typename T> struct Foo
    {
        Foo(int) {}
    };
    
    template <typename T> void magic(Foo<T> const &);
    
    int main()
    {
        magic(10);   // what is T?
    }
    

    2023-01-29 10:34 回答
  • lambda不是a std::function,std::function也不是lambda.

    lambda是一个语法糖来创建一个如下所示的匿名类:

    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);
    

    my_lambda实际上是一些不可命名的类型的类型名称.

    A std::function是完全不同的东西.它是一个operator()使用特定签名实现的对象,并将智能值语义指针存储到覆盖复制/移动/调用操作的抽象接口.它有一个template构造函数,可以采用任何支持copy/move/operator()with兼容签名的类型,生成一个实现抽象内部接口的具体自定义类,并将其存储在上面提到的内部值语义智能指针中.

    然后它将操作从自身转发为值类型到抽象内部指针,包括完美转发到调用方法.

    实际上,您可以将lambda存储在a中std::function,就像存储函数指针一样.

    但是有一大堆不同的东西std::function可以存储一个给定的lambda - 任何类型可以转换为参数和从参数工作的东西,并且实际上同样有效,就所std::function涉及的而言.

    在C++中template的类型推导在s中不起作用"你可以转换成" - 它是模式匹配,纯粹和简单.由于lambda是一种与any无关的类型std::function,因此std::function不能从中推导出任何类型.

    如果C++试图在一般情况下这样做,则必须反转图灵完备过程以确定可以将哪些(如果有的话)类型集传递给它template以生成与转换兼容的实例.

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

    2023-01-29 10:34 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有