【问题标题】:Lambda capture list and defined function pointer type [duplicate]Lambda捕获列表和定义的函数指针类型[重复]
【发布时间】:2016-11-12 14:05:14
【问题描述】:

以下代码无法编译:

typedef void(*RunnableFun)(int);  //pointer-to-function type

void foo(RunnableFun f) {
}
void bar(const std::string& a) {
    foo([&](int) -> void { std::cout << a; });
}

IntelliSense 告诉我

no suitable conversion function from "lambda []void (int)->void" to "RunnableFun" exists

编译器在抱怨

  'void foo(RunnableFun)' : cannot convert argument 1 from 'bar::<lambda_796873cf40a6be4e411eb9df14f486bf>' to 'RunnableFun'

但以下内容确实可以编译:

typedef void(*RunnableFun)(int);  //pointer-to-function type

void foo(RunnableFun f) {

}
void bar(const std::string&) {
    // Notice the empty capture list
    foo([](int) -> void { std::cout << "dummy"; });
}

如何在保留foo() 的签名的同时实现我在第一个代码示例中尝试的效果?

P.S.:将 foo 的签名更改为 void foo(std::function&lt;void(int)&gt; f) 会编译,但我可以不更改它吗?

【问题讨论】:

    标签: c++ lambda


    【解决方案1】:

    您可以查看大部分standard library algorithm functions。当他们采用“谓词”(可调用对象)时,他们将其作为模板参数。

    所以你可以将你的函数作为一个模板:

    template<typename FunctionType>
    void foo(FunctionType f) {
    }
    

    无需其他更改。

    要么这个要么用std::function,不改foo函数就解决不了你的问题。

    【讨论】:

      【解决方案2】:

      指向函数的指针只存储执行位置,不存储其他状态。执行之间的唯一变化必须由 global 状态决定。

      在您的情况下,您希望 lambda 捕获 local 状态。因此,在a="hello" 之后调用时,它打印的值与在a="world" 之后调用时不同。

      简短的回答是“太糟糕了,太伤心了,但没有”。

      你可以破解一下。您可以将a 存储在static std::string ga; 中,并在lambda 中访问mit。请注意,这很丑陋,不可重入,不必要地将代码暴露给全局状态,通常是个坏主意。

      void bar(const std::string& a) {
        static std::string ga;
        ga = a; // note, separate line
        foo([](int) -> void { std::cout << ga; });
      }
      

      通常,纯函数指针回调 API 是一个无知的傻瓜设计 API 的标志:正确的 C 风格回调采用 void*,正确的 C++ 回调是 std::function 或模板可调用或类似的。 (无知,因为他们不知道void* 模式很常见:傻瓜,因为即使您不知道它,他们在尝试了几次他们的系统后也没有自己解决它,并注意到了一个大洞。它是好的:大多数程序员都是傻瓜,直到他们自己犯了所有错误!)

      如果您实际上有一个void* 或等效的参数被您回叫,那么您在问题中省略了它,答案就完全不同了。只需在 void* 中存储一个 ptr-to-lambda,然后存储一个无状态 lambda,它会从 void* 转换并调用有状态 lambda。

      【讨论】:

      • "void* 中转换并调用有状态的 lambda" 我很好奇这到底是怎么发生的。毕竟,lambda 的类型是未知的,因此您需要使用decltype(some_variable) 来获取它并执行转换。那么你从哪里得到变量呢?我认为最好将有状态的 lambda 包装在 std::function 中并将 that 作为有问题的 void* 传递。这样,您就可以确切地知道要将其转换为什么类型。
      • @nicol auto f=[&amp;](int x){std::cout&lt;&lt;a&lt;&lt;'\n';}; foo( [](void* ptr, int x){ auto* pf = static_cast&lt;decltype(f)*&gt;(ptr); (*pf)(x); }, &amp;f );?或者将 lambda-and-void* 模式包装到一个带有工厂函数的类中,该工厂函数可以推断出F 的类型?这里基本上没有什么难的,你需要将 lambda 存储在某个地方,所以decltype 有一个自然目标(无论它在哪里)。
      • 有趣的 hack,虽然不适用于我的 multithreaded application。标记为答案,因为它有很多细节
      • @phillab 在多线程中,可以使用线程本地存储。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-30
      • 2011-08-01
      • 2020-01-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多