40

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.

3
  • 4
    Rules for auto deduction are very similar to the template deduction. Here you have deduction and conversion: you can only have one. Commented Jun 8, 2016 at 18:23
  • 1
    I want to change the question title to "Why does auto * not deduce a function pointer type from a lambda?" in order to make this a concrete question. That seems to be what was puzzling you. But would you prefer something more like "How do I get a function pointer from a lambda without writing the type?" Commented Jun 9, 2016 at 9:35
  • That first title sounds good. Commented Jun 9, 2016 at 13:28

3 Answers 3

51

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; };
Sign up to request clarification or add additional context in comments.

9 Comments

What does this do? Never seen this kind of + syntax before.
Thanks - now I'll have to use it at least once. Reviewer's gonna hate me.
@Jaa-c just don't auto *d = ++[](int i){ return i; }; -- a unary plus too many!
plus 1 for +, I forgot about that.
Note that + fails on at least some MSVC. Because their lambdas have four conversion operators to function pointers :/
|
8

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?

Comments

5

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.

2 Comments

"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;
@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;};)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.