I Cannot Pass Lambda as Std::Function

I cannot pass lambda as std::function

It's because a lambda function is not a std::function<...>. The type of

auto lambda = [](const std::string& s) { return std::stoi(s); };

is not std::function<int(const std::string&)>, but something unspecified which can be assigned to a std::function. Now, when you call your method, the compiler complains that the types don't match, as conversion would mean to create a temporary which cannot bind to a non-const reference.

This is also not specific to lambda functions as the error happens when you pass a normal function. This won't work either:

int f(std::string const&) {return 0;}

int main()
{
std::vector<int> vec;
C<int> c;
c.func(vec, f);
}

You can either assign the lambda to a std::function

std::function<int(const std::string&)> lambda = [](const std::string& s) { return std::stoi(s); };

,change your member-function to take the function by value or const-reference or make the function parameter a template type. This will be slightly more efficient in case you pass a lambda or normal function pointer, but I personally like the expressive std::function type in the signature.

template<typename T>
class C{
public:
void func(std::vector<T>& vec, std::function<T( const std::string)> f){
//Do Something
}

// or
void func(std::vector<T>& vec, std::function<T( const std::string)> const& f){
//Do Something
}

// or
template<typename F> func(std::vector<T>& vec, F f){
//Do Something
}
};

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

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:

why can't I convert a lambda to a std::function here?

std::function<int()> accepts a function which takes in no arguments and returns an int. Your proposed lambda takes in an int and returns an int.

Consider std::function<int(int)> instead:

#include <iostream>
#include <functional>

std::function<int(int)> funcGen(int param) {
std::function<int(int)> myGeneratedFunc =
[param](int input) -> int {
return input+param;
};
return myGeneratedFunc;
}

int main() {

std::function<int(int)> myFunc = funcGen(3);
std::cout << "this should be 4=3+1: " << myFunc(1) << "\n";

return 0;
}

Passing a Lambda Expression to std::function in C++

The problem

The problem you have can be reduced to this:

#include <functional>

template <typename ...Args>
void foo(std::function<bool(Args...)>) { }

int main()
{
foo([](int, int) { return true; });
}

which will fail to compile. The reason for this is that the type deduction for the template arguments for std::function fails.

You expect a std::function of some sort to be passed as an argument. Since no std::function object (of whatever exact instantiation) is passed in, the compiler tries to construct a std::function and deduce the template parameter types by calling the constructor of std::function. Here starts the problem. The matching constructor in this case would be:

template <typename F>
function(F f);

You will notice that the constructor itself is also templated. The compiler could successfully deduce F as a lambda, but since F is a template parameter of the constructor, the template parameter of the class itself std::function cannot be deduced.

Furthermore, to quote cppreference on that constructor:

[...] This constructor does not participate in overload resolution
unless f is Callable for argument types Args... and return type R. [...]

This means that the existance of this constructor is based on whether F can be called with the class template arguments Args..., but since those are not explicitly defined and cannot be deduced, this constructor won't be available anyway.

The solution

Since you only use that std::function inside exportSelectedData, simply make it a template parameter alltogether (ditching the std::function part):

template<typename Func, typename ...Args_T>
std::vector<CSingleElement> exportSelectedData(uint32_t u32MutexTimeout, Func compareFn, Args_T const&...) const;

You should also change Args_T&& to Args_T const& since you don't simply forward those arguments but reuse them inside a loop.

Edit

Regarding your follow-up question in the edit: think about what you're doing.

First you declare a lambda:

auto lambda = [u32MaxCmdDuration](const CombinedDictElement& singleElement) { /* ... */ };

Now think about the signature of that lambda. You return a boolean so the return type is bool (so far so good). You take u32MaxCmdDuration in the capture-clause and you take one argument singleElement. Lets remove all the extra qualifiers and look at the signature:

bool(CombinedDictElement) // take a CombinedDictElement and return a bool

Next, we take a look at the call of exportSelectedData:

exportSelectedData(u32MutexTimeout, lambda, u32MaxCmdDuration);

You pass in u32MutexTimeout and lambda which is perfectly fine, the lambda is captued by compareFn. The third argument is u32MaxCmdDuration which is captured by ...compareArgs in your template. Now lets take a look at where you actually invoke the lambda inside exportSelectedData:

if (compareFn(singleElement, compareArgs...)) // ...

What signature do you expect compareFn to have here? If we expand the ...compareArgs pack (again, removing the extra qualifiers for simplicity) the signature looks like this:

bool(CombinedDictElement, unsigned int) // take a CombinedDictElement and an unsigned int and return a bool

Here is the lambda signature again:

bool(CombinedDictElement)

Do you spot the problem? The lambda captures u32MaxCmdDuration as a state capture while exportSelectedData expects it as an parameter (since you passed it into exportSelectedData as an additional argument). The signatures differ from each other, obviously, so we have to change one of them to match the other. This is fairly easy in your case, either

  • change your lambda to take u32MaxCmdDuration as a parameter:

    auto lambda = [](const CombinedDictElement& singleElement, unsigned int u32MaxCmdDuration)

    or

  • remove u32MaxCmdDuration as an additional argument to your exportSelectedData call:

     exportSelectedData(u32MutexTimeout, lambda);

Must I use lambda to pass a member function as std::function?

std::bind is the classical approach, that avoids the need to explicitly spell out the forwarding signature:

using namespace std::placeholders;

// ...

worker(std::bind(&Caller::receiver, this, _1, _2, _3));

C++20 also has std::bind_front; it reduces this verbiage, somewhat.

You cannot pass a pointer to a member function "directly". This is because a member function requires a specific instance of an object whose member function should get invoked. So, in some form of fashion, in some way, you cannot avoid this, or some other instance of the object, to be involved in the process. This is fundamental to C++. The only thing that can be done here is to find some syntactic sugar, which is basically all this is, here.

std::functions and lambda function passing

Note that (1) fn is defined as reference (to const); (2) lambda and std::function are not the same type; (3) You can't bind reference to object with different type directly.

For the 1st case,

TestClass t([](std::string str) {std::cout << str << std::endl; });
t.F();

A temporary lambda is created and then converted to std::function which is a temporary too. The temporary std::function is bound to the parameter _f of the constructor and bound to member f. The temporary will be destroyed after this statement, then f becomes dangled, when t.F(); it fails.

For the 2nd case,

fn __f = [](std::string str) {std::cout << str << std::endl; };
TestClass t(__f);
t.F();

A temporary lambda is created and then bound to reference (to const). Then its lifetime is extended to the lifetime of the reference __f, so the code is fine.

For the 3rd case,

auto __f = [](std::string str) {std::cout << str << std::endl; };
TestClass t(__f);
t.F();

lambda is created and then converted to std::function which is a temporary. The temporary std::function is bound to the parameter _f of the constructor and bound to member f. The temporary will be destroyed after this statement, then f becomes dangled, when t.F(); it fails.



(1) You could declare fn as non-reference like typedef std::function<void(std::string)> fn;, then std::function will be copied and every case would work well.

(2) Don't use names begin with double underscore, they're reserved in C++.

I can't pass lambda with reference capture

You can only do the above with capture-less lambdas.

See [expr.prim.lambda.closure] (sec 7)

The closure type for a non-generic lambda-expression with no
lambda-capture
whose constraints (if any) are satisfied has a
conversion function to pointer to function
with C++ language linkage
having the same parameter and return types as the closure type's
function call operator.

Since lambdas are not just ordinary functions and capturing it need to preserve a state,
you can not find any simple or conventional solution to make them assign to function pointers.


To fix, you can use std::function which will do it by type erasure:

#include <functional> // std::function

int test;
std::function<float(int)> f = [&](int i) -> float {return static_cast<float>(test); };

How to convert a lambda to an std::function using templates

You can't pass a lambda function object as an argument of type std::function<T> without explicitly specifying the template argument T. Template type deduction tries to match the type of your lambda function to the std::function<T> which it just can't do in this case - these types are not the same. Template type deduction doesn't consider conversions between types.

It is possible if you can give it some other way to deduce the type. You can do this by wrapping the function argument in an identity type so that it doesn't fail on trying to match the lambda to std::function (because dependent types are just ignored by type deduction) and giving some other arguments.

template <typename T>
struct identity
{
typedef T type;
};

template <typename... T>
void func(typename identity<std::function<void(T...)>>::type f, T... values) {
f(values...);
}

int main() {
func([](int x, int y, int z) { std::cout << (x*y*z) << std::endl; }, 3, 6, 8);
return 0;
}

This is obviously not useful in your situation though because you don't want to pass the values until later.

Since you don't want to specify the template parameters, nor do you want to pass other arguments from which the template parameters can be deduced, the compiler won't be able to deduce the type of your std::function argument.

Lambdas and std::function

Stephan T. Lavavej explains why this doesn't work in this video. Basically, the problem is that the compiler tries to deduce BaseT from both the std::vector and the std::function parameter. A lambda in C++ is not of type std::function, it's an unnamed, unique non-union type that is convertible to a function pointer if it doesn't have a capture list (empty []). On the other hand, a std::function object can be created from any possible type of callable entity (function pointers, member function pointers, function objects).

Note that I personally don't understand why you would want to limit the incoming functors to that specific signature (in addition to the fact that indirection through a polymorphic function wrapper, like std::function, is by far more inefficient than a direct call to a functor (which may even be inlined)), but here's a working version. Basically, it disables argument deduction on the std::function part, and only deduces BaseT from the std::vector argument:

template<class T>
struct Identity{
typedef T type;
};

template<typename BaseT>
vector<BaseT> findMatches(vector<BaseT> search,
typename Identity<function<bool (const BaseT &)>>::type func)
{
vector<BaseT> tmp;

for(auto item : search)
{
if( func(item) )
{
tmp.push_back(item);
}
}

return tmp;
}

Live example on Ideone.

Another possible way would be to not restrict the functor type directly, but indirectly through SFINAE:

template<class T, class F>
auto f(std::vector<T> v, F fun)
-> decltype(bool(fun(v[0])), void())
{
// ...
}

Live example on Ideone.

This function will be removed from the overload set if fun doesn't take an argument of type T& or if the return type is not convertible to bool. The , void() makes f's return type void.

Cannot pass lambda function as function reference?

A lambda is not really a function, it's a closure object. Basically, a compiler-generated class with operator() defined. A non-capturing closure also defines a conversion operator for converting to good old pointer-to-function.



Related Topics



Leave a reply



Submit