Obtaining function pointer to lambda?

10,705

Solution 1

This fails:

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

because the lambda is not a pointer. auto does not allow for conversions. Even though the lambda is convertible to something that is a pointer, that's not going to be done for you - you have to do it yourself. Whether with a cast:

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

Or with some sorcery:

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

Solution 2

Especially when it can implicitly convert a unique, lambda type to a function pointer:

But it cannot convert it to "a function pointer". It can only convert it to a pointer to a specific function signature. This will fail:

int (*h)(float) = a;

Why does that fail? Because there is no valid implicit conversion from a to h here.

The conversion for lambdas is not compiler magic. The standard simply says that the lambda closure type, for non-capturing, non-generic lambdas, has an implicit conversion operator for function pointers matching the signature of its operator() overload. The rules for initializing int (*g)(int) from a permit using implicit conversions, and thus the compiler will invoke that operator.

auto doesn't permit using implicit conversion operators; it takes the type as-is (removing references, of course). auto* doesn't do implicit conversions either. So why would it invoke an implicit conversion for a lambda closure and not for a user-defined type?

Solution 3

The lambda code doesn't work for the same reason this doesn't work:

struct foo {
  operator int*() const {
    static int x;
    return &x;
  }
};

int* pint = foo{};
auto* pint2 = foo{}; // does not compile

or even:

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

The lambda has an operator that implicitly converts it to a (particular) function pointer, just like foo.

auto does not do conversion. Ever. Auto behaves like a class T parameter to a template function where its type is deduced.

As the type on the right hand side is not a pointer, it cannot be used to initialize an auto* variable.

Lambdas are not function pointers. Lambdas are not std::functions. They are auto-written function objects (objects with an operator()).

Examine this:

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

it compiles and works in gcc (not certain if it is an extension, now that I think about it). However, what would auto* ptr = [](auto x){std::cout << x;} supposed to do?

However, unary + is an operator that works on pointers (and does nearly nothing to them), but not in foo or a lambda.

So

auto* pauto=+foo{};

And

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

Both work, magically.

Share:
10,705
oconnor0
Author by

oconnor0

Updated on June 04, 2022

Comments

  • oconnor0
    oconnor0 almost 2 years

    I want to be able to obtain a function pointer to a lambda in C++.

    I can do:

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

    And, of course, the following works - even if it's not creating a function pointer.

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

    But the following:

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

    Gives this error in 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)>'
    

    It seems arbitrary that a lambda can be converted to a function pointer without issue, but the compiler cannot infer the function type and create a pointer to it using auto *. Especially when it can implicitly convert a unique, lambda type to a function pointer:

    int (*g)(int) = a;
    

    I've create a little test bed at http://coliru.stacked-crooked.com/a/2cbd62c8179dc61b that contains the above examples. This behavior is the same under C++11 and C++14.

  • Jaa-c
    Jaa-c almost 8 years
    What does this do? Never seen this kind of + syntax before.
  • Barry
    Barry almost 8 years
    @Jaa-c Added a link.
  • Barry
    Barry almost 8 years
    "not certain if it is an extension" I'd expect that to work. You can take a specific function pointer to a function template (e.g. template <class T> void foo(T) { } void(*p)(int) = foo;
  • Jaa-c
    Jaa-c almost 8 years
    Thanks - now I'll have to use it at least once. Reviewer's gonna hate me.
  • Yakk - Adam Nevraumont
    Yakk - Adam Nevraumont almost 8 years
    @Barry Sure I expect that to work. But auto lambdas are relatively new, and clang rejects it. Me, I'd even want implicit cast to any function that is convertion-compatible, so even void(*f)(double) = [](int x){std::cout << x;}; would compile (like it does with std::function<void(double) f = [](int x){std::cout << x;};)
  • Yakk - Adam Nevraumont
    Yakk - Adam Nevraumont almost 8 years
    @Jaa-c just don't auto *d = ++[](int i){ return i; }; -- a unary plus too many!
  • KyleKnoepfel
    KyleKnoepfel almost 8 years
    To steal someone else's line from I-don't-know where: this is awesome, yet totally messed up.
  • Yakk - Adam Nevraumont
    Yakk - Adam Nevraumont almost 8 years
    plus 1 for +, I forgot about that.
  • Chris Beck
    Chris Beck almost 8 years
    @Yakk: I am pleasantly surprised that both ++[] ... and +++[] fail to compile, I guess the + thing is less evil than I thought at first glance
  • tomsmeding
    tomsmeding almost 8 years
    @Chris Maybe because those make a prefix increment operator instead of a unary plus operator? ;)
  • T.C.
    T.C. almost 8 years
    Note that + fails on at least some MSVC. Because their lambdas have four conversion operators to function pointers :/