【问题标题】:Obtaining function pointer to lambda?获取指向 lambda 的函数指针?
【发布时间】:2016-10-09 03:46:05
【问题描述】:

我希望能够在 C++ 中获得指向 lambda 的函数指针。

我能做到:

int (*c)(int) = [](int i) { return i; };

而且,当然,以下工作 - 即使它没有创建函数指针。

auto a = [](int i) { return i; };

但是如下:

auto *b = [](int i) { return i; };

在 GCC 中给出这个错误:

main.cpp: In function 'int main()':
main.cpp:13:37: error: unable to deduce 'auto*' from '<lambda closure object>main()::<lambda(int)>{}'
     auto *b = [](int i) { return i; };
                                      ^
main.cpp:13:37: note:   mismatched types 'auto*' and 'main()::<lambda(int)>'

似乎可以毫无问题地将 lambda 转换为函数指针,但编译器无法推断函数类型并使用 auto * 创建指向它的指针。尤其是当它可以将unique, lambda type 隐式转换为函数指针时:

int (*g)(int) = a;

我在http://coliru.stacked-crooked.com/a/2cbd62c8179dc61b 创建了一个包含上述示例的小测试平台。这种行为在 C++11 和 C++14 下是一样的。

【问题讨论】:

  • auto 的推演规则与模板推演非常相似。在这里你有扣除和转换:你只能有一个。
  • 我想将问题标题更改为“为什么 auto * 不能从 lambda 推导出函数指针类型?”为了使它成为一个具体的问题。这似乎就是让你困惑的地方。但是您是否更喜欢“如何在不编写类型的情况下从 lambda 获取函数指针?”
  • 第一个标题听起来不错。

标签: c++ c++11 lambda c++14


【解决方案1】:

这失败了:

auto *b = [](int i) { return i; };

因为 lambda 不是指针。 auto 不允许转换。即使 lambda 可转换为指针,但这不会为您完成 - 您必须自己完成。是否带演员表:

auto *c = static_cast<int(*)(int)>([](int i){return i;});

或者some sorcery:

auto *d = +[](int i) { return i; };

【讨论】:

  • 这是做什么的?以前从未见过这种+ 语法。
  • 谢谢 - 现在我必须至少使用一次。审稿人会恨我的。
  • @Jaa-c 只是不要 auto *d = ++[](int i){ return i; }; -- 一元加太多!
  • 加 1 表示 +,我忘了。
  • 请注意,+ 至少在某些 MSVC 上失败。因为他们的 lambda 有四个转换运算符到函数指针:/
【解决方案2】:

尤其是当它可以将唯一的 lambda 类型隐式转换为函数指针时:

但它不能将其转换为“函数指针”。它只能将其转换为指向特定函数签名的指针。这将失败:

int (*h)(float) = a;

为什么会失败?因为这里没有从ah 的有效隐式转换。

lambdas 的转换不是编译器的魔法。该标准简单地说,对于非捕获、非泛型 lambda,lambda 闭包类型具有用于匹配其operator() 重载签名的函数指针的隐式转换运算符。从 a 初始化 int (*g)(int) 的规则允许使用隐式转换,因此编译器将调用该运算符。

auto 不允许使用隐式转换运算符;它按原样采用类型(当然,删除引用)。 auto* 也不进行隐式转换。那么为什么它会为 lambda 闭包而不是为用户定义的类型调用隐式转换呢?

【讨论】:

    【解决方案3】:

    lambda 代码不起作用的原因与它不起作用的原因相同:

    struct foo {
      operator int*() const {
        static int x;
        return &x;
      }
    };
    
    int* pint = foo{};
    auto* pint2 = foo{}; // does not compile
    

    甚至:

    template<class T>
    void test(T*) {};
    test(foo{});
    

    lambda 有一个运算符,可以将其隐式转换为(特定的)函数指针,就像foo

    auto 不进行转换。曾经。 Auto 的行为类似于模板函数的 class T 参数,其中推导出其类型。

    因为右边的类型不是指针,所以不能用来初始化auto*变量。

    Lambda 不是函数指针。 Lambda 不是 std::functions。它们是自动编写的函数对象(带有operator() 的对象)。

    检查一下:

    void (*ptr)(int) = [](auto x){std::cout << x;};
    ptr(7);
    

    它在 gcc 中编译和工作(不确定它是否是扩展,现在我考虑了一下)。但是,auto* ptr = [](auto x){std::cout &lt;&lt; x;} 应该怎么做呢?

    但是,一元 + 是一个适用于指针的运算符(几乎对指针不做任何事情),但不适用于 foo 或 lambda。

    所以

    auto* pauto=+foo{};
    

    auto* pfun=+[](int x){};
    

    两者都有效,神奇。

    【讨论】:

    • "不确定它是否是扩展" 我希望它可以工作。您可以将特定的函数指针指向函数模板(例如template &lt;class T&gt; void foo(T) { } void(*p)(int) = foo;
    • @Barry 当然,我希望这能奏效。但是auto lambdas 相对较新,clang 拒绝它。我,我什至想要隐式转换为任何兼容转换的函数,所以即使void(*f)(double) = [](int x){std::cout &lt;&lt; x;}; 也会编译(就像std::function&lt;void(double) f = [](int x){std::cout &lt;&lt; x;}; 一样)
    猜你喜欢
    • 1970-01-01
    • 2015-11-25
    • 2016-03-20
    • 1970-01-01
    • 2022-12-01
    • 2012-02-10
    • 1970-01-01
    • 2011-04-26
    相关资源
    最近更新 更多