C++11 does not deduce type when std::function or lambda functions are involved
The issue is on the nature of lambdas. They are function objects with a fixed set of properties according to the standard, but they are not a function. The standard determines that lambdas can be converted into std::function<>
with the exact types of arguments and, if they have no state, function pointers.
But that does not mean that a lambda is a std::function
nor a function pointer. They are unique types implementing operator()
.
Type deduction, on the other hand, will only deduce exact types, with no conversions (other than const/volatile qualifications). Because the lambda is not a std::function
the compiler cannot deduce the type in the call: filter(mySet,[](int i) { return i%2==0; });
to be any std::function<>
instantiation.
As of the other examples, in the first one you convert the lambda to the function type, and then pass that. The compiler can deduce the type there, as in the third example where the std::function
is an rvalue (temporary) of the same type.
If you provide the instantiating type int
to the template, second working example, deduction does not come into play the compiler will use the type and then convert the lambda to the appropriate type.
Failure to deduce template argument std::function from lambda function
A std::function
is not a lambda, and a lambda is not a std::function
.
A lambda is an anonymous type with an operator()
and some other minor utility. Your:
auto foo = [](int i) {
std::cout << i << std::endl;
};
is shorthand for
struct __anonymous__type__you__cannot__name__ {
void operator()(int i) {
std::cout << i << std::endl;
}
};
__anonymous__type__you__cannot__name__ foo;
very roughly (there are actual convert-to-function pointer and some other noise I won't cover).
But, note that it does not inherit from std::function<void(int)>
.
A lambda won't deduce the template parameters of a std::function
because they are unrelated types. Template type deduction is exact pattern matching against types of arguments passed and their base classes. It does not attempt to use conversion of any kind.
A std::function<R(Args...)>
is a type that can store anything copyable that can be invoked with values compatible with Args...
and returns something compatible with R
.
So std::function<void(char)>
can store anything that can be invoked with a char
. As int
functions can be invoked with a char
, that works.
Try it:
void some_func( int x ) {
std::cout << x << "\n";
}
int main() {
some_func('a');
some_func(3.14);
}
std::function
does that some conversion from its signature to the callable stored within it.
The simplest solution is:
template <class F, class T>
void call(F f, T v) {
f(v);
}
now, in extremely rare cases, you actually need the signature. You can do this in c++17:
template<class T>
void call(std::function<void(T)> f, T v) {
f(v);
}
template<class F, class T>
void call(F f_in, T v) {
std::function f = std::forward<F>(f_in);
call(std::move(f), std::forward<T>(v));
}
Finally, your call
is a crippled version of std::invoke
from c++17. Consider using it; if not, use backported versions.
why do lambda functions in c++11 not have function types?
std::function
is a tool useful to store any kind of callable object regardless of its type. In order to do this it needs to employ some type erasure technique, and that involves some overhead.
Any callable can be implicitly converted to a std::function
, and that's why it usually works seamlessly.
I'll repeat to make sure it becomes clear: std::function
is not something just for lambdas or function pointers: it's for any kind of callable. That includes things like struct some_callable { void operator()() {} };
, for example. That is a simple one, but it could be something like this instead:
struct some_polymorphic_callable {
template <typename T>
void operator()(T);
};
A lambda is just yet another callable object, similar to instances of the some_callable
object above. It can be stored in a std::function
because it's callable, but it doesn't have the type erasure overhead of std::function
.
And the committee plans to make lambdas polymorphic in the future, i.e., lambdas that look like some_polymorphic_callable
above. Which std::function
type would such a lambda be?
Now... Template parameter deduction, or implicit conversions. Pick one. That's a rule of C++ templates.
To pass a lambda as a std::function
argument, it needs to be implicitly converted. Taking a std::function
argument means that you're choosing implicit conversions over type deduction. But your function template needs the signature to be deduced or provided explicitly.
The solution? Don't restrict your callers to std::function
. Accept any kind of callable.
template <typename Fun>
auto flip(Fun&& f) -> decltype(std::bind(std::forward<Fun>(f),_2,_1))
{ return std::bind(std::forward<Fun>(f),_2,_1); }
You may now be thinking why do we need std::function
then. std::function
provides type erasure for callables with a known signature. That essentially makes it useful to store type-erased callables and to write virtual
interfaces.
Why can't the compiler deduce to this function template?
Lambdas in C++ are instances of an anonymous class specifically created for each lambda object, this means that two lambdas with the same code are actually different objects.
This also means that you can't pass lambdas around without using a template because there's no lambda type. Lambda is neither std::function
( and is not derived from it) nor a function pointer. They are unique types implementing application operator, i.e operator()
.
You can do it using static_cast operator
template <typename T>
void foo(std::function<void(T)> bar)
{
}
foo( static_cast< std::function<void(float)> >( [](float number){}));
//or
foo( std::function<void(float)>{ [](float){} } );
Why cannot use `decltype()` for `std::function` with lambda?
This is simply improper usage of the std::function
wrapper. This type is meant to hide the actual implementation by wrapping it into a type-erased object with as little information about the underlying callable as possible: and this is the function signature.
When you use decltype(f)
, you get the acutal unique, compiler-generated type of the lambda expression. But this is not how you can instantiate a std::function
, as the basic, non-specialized template template<class> std::function
is undefined. Only the specialization template<class R, class ...Args> std::function<R(Args...)>
is defined, and you cannot instantiate this with the type of a lambda expression.
Do note that all lambda expression have a signature, too: they accept some arguments (possibly template parameter types) and return a value of some type. Those is the information you need to put into the std::function
instantiation. In your case int(int)
as you posted yourself.
Why cant we declare std::function with auto
auto
in a lambda parameter list doesn't represent one single automatically-inferred type like it would in a variable initialization, it represents that the lambda has a templated operator()
which has a whole parameterized family of function signatures.
You can't instantiate a template that expects a concrete type (and std::function
does) with a parameterized family of types. You could create a parameterized family of typedefs, each formed by instantiating std::function
:
template<con1 T> using function_signature = std::function<void (T)>;
But this doesn't get you any closer to being able to write R<function_signature>
. For that, you'd need template<template class T<U>> class R;
and then R
needs to somehow provide the type parameter T2
in T<T2> functor
.
In the end, it comes down to std::function
being a wrapper to a pointer-to-member function (among other flavors), and a pointer-to-member function cannot point to a whole template family of member functions.
This also fails:
auto lambda_1 = [](con1 auto functor){....};
auto pmf = &lambda_1::operator(); // cannot take address of template member function
Put another way, type-erasure doesn't work on templates. Instantiating the template requires its type.
Why does std::apply fail with function template, but not with a lambda expression with explicit template parameter list?
A function template is not a function, just like a cookie cutter is not a cookie. C++ templates create new functions for every set of template arguments. And this is precisely why add_generic
is not a viable argument. It's not a function, it cannot be passed around as a value. To obtain the function, the compiler needs to deduce the template arguments. But it can only do that inside apply
after the callable has been passed. Chicken and egg right there.
Why does a lambda work? Because it's not a plain function either. It produces a hidden object type
struct __lambda_at_line_N {
template<typename T>
auto operator()(T first, T second) { /* ... */ }
};
And passes that
std::apply(__lambda_at_line_N{},std::make_pair(2.0,3.0));
This is an actual object, a value. Its type does not depend on the template argument deduction that needs to happen inside std::apply
to call operator()
. That's why it works.
return type deduction of lambda function not working
Because the implicit conversion (from lambda to function pointer) won't be considered in template argument deduction, the template parameter T
can't be deduced and the invocation fails.
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
Except for specifying template argument double
explicitly like call<double>(...);
, you can convert the lambda to function pointer explicitly, e.g.
call(
static_cast<double(*)()>(
[](void) -> double
{ return 1.0;
})
);
or
call(
+[](void) -> double
{ return 1.0;
}
);
Related Topics
Sorting Zipped (Locked) Containers in C++ Using Boost or the Stl
Why Does Subtracting '0' in C Result in the Number That the Char Is Representing
Reinterpret_Cast Creating a Trivially Default-Constructible Object
What Exactly Is "Broken" With Microsoft Visual C++'S Two-Phase Template Instantiation
"Undefined Reference To" Errors When Linking Static C Library With C++ Code
Error: Free(): Invalid Next Size (Fast):
What Does Casting to 'Void' Really Do
Does "&S[0]" Point to Contiguous Characters in a Std::String
Returning a Reference to a Local or Temporary Variable
Debugging Core Files Generated on a Customer'S Box
Why Does a Static Data Member Need to Be Defined Outside of the Class
Will Std::String Always Be Null-Terminated in C++11
Why Do I Get an Infinite Loop If I Enter a Letter Rather Than a Number
Why C++ Containers Don't Allow Incomplete Types
Why Is My Program Slow When Looping Over Exactly 8192 Elements