Anakhand
Anakhand

Reputation: 2998

Passing a lambda argument to a std::function parameter without intermediate variable

This might look similar to "I cannot pass lambda as std::function", but I'm actually passing the std::function parameter by value, so that problem doesn't apply. I've defined the following function.

template<typename T>
std::vector<T> countSort(const std::vector<T> &v, std::function<int(T)> keyFunc, int n);

The second parameter is an std::function that maps T to int (passed by value).

When calling this, I wanted to use a lambda expression, as follows:

std::vector<int> v;
[...]
v = countSort(v, [](int x) { return x; }, 10);

But the template argument deduction fails, because "main()::<lambda(int)> is not derived from std::function<int(T)>". It does work if I specify the template argument, or if I introduce an intermediate variable of type std::function for the lambda expression:

std::function<int(int)> lambda = [](int x) { return x; };
v = countSort(v, lambda, 10);

Why can't I do the former? I'm giving the compiler the exact same information; if it is able to convert a value of type lambda<int> to std::function<int(int)> when assigning it to a variable, why can't it directly convert from lambda<int> to the parameter type, which is std::function<T(int)>—and taking into account that v is of type std::vector<int>, it should know that T is int? The whole reason I want to use a lambda expression is precisely that, it's an expression, so I should be able to write it inline in the function call argument list, without having to give it a name or assign it to a variable.

Upvotes: 1

Views: 1298

Answers (1)

songyuanyao
songyuanyao

Reputation: 172884

The problem is, template argument deduction doesn't consider implicit conversion (from lambda to std::function), which causes the deduction for T on the 2nd function parameter keyFunc to fail.

Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.

You can use std::type_identity (since C++20) to exclude the 2nd function parameter from deduction. e.g.

template<typename T>
std::vector<T> countSort(const std::vector<T> &v, std::function<int(std::type_identity_t<T>)> keyFunc, int n);

BTW: If your compiler doesn't support std::type_identity, it's not hard to make one.

And about how std::type_identity works here, see non-deduced context:

(emphasis mine)

In the following cases, the types, templates, and non-type values that are used to compose P do not participate in template argument deduction, but instead use the template arguments that were either deduced elsewhere or explicitly specified. If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.

  1. The nested-name-specifier (everything to the left of the scope resolution operator ::) of a type that was specified using a qualified-id:

Upvotes: 7

Related Questions