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
}
};
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
.
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++.
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 yourexportSelectedData
call:exportSelectedData(u32MutexTimeout, lambda);
Possible storage waste of storing lambda into std::function
This is the price you pay for not needing to know the type of the function. All std::function<void()>
s are interchangeable no matter which lambda they came from. If you want to store lots of the same type of function (with different captures) in a vector, you can make it a functor instead of a lambda (so that it has a name) and make a vector of that type.
Example: With lambda:
std::vector<std::function<void()>> funcs;
for(int i = 0; i < 10000; i++)
funcs.push_back([i]() {std::cout << i << std::endl;});
for(auto& func : funcs)
func();
With functor:
struct number_printing_function {
int i;
number_printing_function(int i) : i(i) {}
void operator()() {
std::cout << i << std::endl;
}
};
std::vector<number_printing_function> funcs;
for(int i = 0; i < 10000; i++)
funcs.push_back(number_printing_function(i));
// or funcs.emplace_back(i);
for(auto& func : funcs)
func();
IMO this is a bit useless, because we might as well store a vector of ints and stop pretending they are functions. When you have many functors of the same type you already know what they do, so just do it. Really, the code above is just the code below, but with extra steps:
std::vector<int> ints;
for(int i = 0; i < 10000; i++)
ints.push_back(i);
for(auto& i : ints)
std::cout << i << std::endl;
If lambdas don't have a specified type, how does std::function accept a lambda?
The type is there. It’s just that you don’t know in advance what it is. Lambdas have type - just the standard says nothing about what that type is; it only gives the contracts that type has to fulfill. It’s up to the compiler implementers to decide what that type really is. And they don’t have to tell you. It’s not useful to know.
So you can deal with it just like you would deal with storage of any “generic” type. Namely: provide suitably aligned storage, then use placement new
to copy-construct or move-construct the object in that storage. None of it is specific to std::function
. If your job was to write a class that can store an arbitrary type, you’d do just that. And it’s what std::function
has to do as well.
std::function
implementers usually employ the small-object optimization. The class leaves some unused room in its body. If the object to be stored is of an alignment for which the empty room is suitable, and if it will fit in that unused room, then the storage will come from within the std::function
object itself. Otherwise, it’ll have to dynamically allocate the memory for it. That means that e.g. capture of intrinsic vector types (AVX, Neon, etc) - if such is possible - will usually make a lambda unfit for small object optimization storage within std::function
.
I'm making no claims as to whether or if the capture of intrinsic vector types is allowed, fruitful, sensible, or even possible. It's sometimes a minefield of corner cases and subtle platform bugs. I don't suggest anyone go and do it without full understanding of what's going on, and the ability to audit the resulting assembly as/when needed (implication: under pressure, typically at 4AM on the demo day, with the suits about to wake up soon - and already irked that they have to interrupt their golf play so early in the day just to watch the presenter sweat).
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 composeP
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.
- The nested-name-specifier (everything to the left of the scope
resolution operator::
) of a type that was specified using a
qualified-id:
What's the difference between lambda and std::function?
The first f
(i.e., the one designated with auto
) results to what is called a lambda function. Also knows as a closure. Closures are unnamed function objects. That's why we need auto
to deduce the type of a closure. We don't know it's type but the compiler does. Thus, by using auto
we let the compiler deduce the type of the unnamed closure object for us.
The second f
(i.e., the one designated with std::function
) is a std::function
object. Class std::function is a general-purpose polymorphic function wrapper.
Lambdas closures as function objects can be converted to their respective std::function
objects. That is exactly what is happening in:
std::function<void(int, int)> f = [](int some, int some2) {
//do something
}
The lambda closure on the right hand side is assigned and converted to the std::function
object on the left side of the assignment.
Practically, they're both interpreted as functors, since they both overload call operator()
and thus can be called, except for that the lambda's type is unnamed.
Another difference between those two is that you can't assign between lambda closures, since for lambda closures the assignment operator is declared deleted. while you can assign between std::function
objects.
C++ lambda as std::function in template function
What about simply as follows ?
template <typename F>
auto Context::executeTransient (F const & commands) {
...
auto result = commands(commandBuffer);
...
return result;
}
This way your method accept both standard functions and lambdas (without converting them to standard functions, that is preferable, from the performance point of view (as far as I know)) and the return type is deduced from the use (auto
).
In you need to know the R
type inside the method, you can apply decltype()
to result
auto result = commands(commandBuffer);
using R = decltype(result);
If you need to know the R
type as template parameter of the method, its a little more complex because involve std::declval()
and, unfortunately, add redundancy
template <typename F,
typename R = decltype(std::declval<F const &>()(commandBuffer))>
R Context::executeTransient (F const & commands) {
...
R result = commands(commandBuffer);
...
return result;
}
Call a function with std::function as argument with a lambda
As written in the linked answer, the first version does not compile, because template argument deduction fails for the first argument; a lambda is never an std::function<void(T)>
.
The second version compiles, because, by writing {foo}
, std::function<void(T)>
becomes a non-deduced context. So deduction can no longer fail for the first argument, as it isn't even attempted. So T
is deduced as int
solely from the second argument, and the call succeeds, because the lambda is convertible to an std::function<void(int)>
.
From [temp.deduct.type]:
The non-deduced contexts are:
- ...
- A function parameter for which the associated argument is an initializer list but the parameter
does not have a type for which deduction from an initializer list is specified.
Related Topics
How to Get Hwnd of Window Opened by Shellexecuteex.. Hprocess
C++ Access Violation Reading Location 0Xcdcdcdcd Error on Calling a Function
Why Is Using Exit() Considered Bad
How Does the Stl's Multimap Insert Respect Orderings
C/C++ Counting the Number of Decimals
How to Simulate Printf's %P Format When Using Std::Cout
Differencebetween If (Null == Pointer) VS If (Pointer == Null)
Std::Stod Throws Out_Of_Range Error for a String That Should Be Valid
Will Memcpy or Memmove Cause Problems Copying Classes
How to Implement Interfaces in C++
Directx/C++ 3D Engine Programming: Learn Now, or Wait for Directx 12
Converting Integer into Array of Digits
Why Can't I Use Inheritance to Implement an Interface in C++
C++ Map Access Discards Qualifiers (Const)
Simple String Parsing with C++