Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass lambda as template function parameter

Why doesn't the following code compile (in C++11 mode)?

#include <vector>

template<typename From, typename To>
void qux(const std::vector<From>&, To (&)(const From&)) { }

struct T { };

void foo(const std::vector<T>& ts) {
    qux(ts, [](const T&) { return 42; });
}

The error message is:

prog.cc:9:5: error: no matching function for call to 'qux'
    qux(ts, [](const T&) { return 42; });
    ^~~
prog.cc:4:6: note: candidate template ignored: could not match 'To (const From &)' against '(lambda at prog.cc:9:13)'
void qux(const std::vector<From>&, To (&)(const From&)) { }
     ^

But it doesn't explain why it couldn't match the parameter.

If I make qux a non-template function, replacing From with T and To with int, it compiles.

like image 964
emlai Avatar asked Jan 29 '17 12:01

emlai


People also ask

Can you template a lambda?

Lambda-expressions are not allowed in unevaluated expressions, template arguments, alias declarations, typedef declarations, and anywhere in a function (or function template) declaration except the function body and the function's default arguments.

How do you pass a lambda function as a parameter?

Basically to pass a lamda expression as a parameter, we need a type in which we can hold it. Just as an integer value we hold in primitive int or Integer class. Java doesn't have a separate type for lamda expression instead it uses an interface as the type to hold the argument.


2 Answers

A lambda function isn't a normal function. Each lambda has its own type that is not To (&)(const From&) in any case.
A non capturing lambda can decay to To (*)(const From&) in your case using:

qux(ts, +[](const T&) { return 42; });

As noted in the comments, the best you can do to get it out from a lambda is this:

#include <vector>

template<typename From, typename To>
void qux(const std::vector<From>&, To (&)(const From&)) { }

struct T { };

void foo(const std::vector<T>& ts) {
    qux(ts, *+[](const T&) { return 42; });
}

int main() {}

Note: I assumed that deducing return type and types of the arguments is mandatory for the real problem. Otherwise you can easily deduce the whole lambda as a generic callable object and use it directly, no need to decay anything.

like image 169
skypjack Avatar answered Oct 05 '22 23:10

skypjack


If you don't need to use the deduced To type, you can just deduce the type of the whole parameter:

template<typename From, typename F>
void qux(const std::vector<From>&, const F&) { }
like image 42
emlai Avatar answered Oct 05 '22 23:10

emlai