C++11 Does Not Deduce Type When Std::Function or Lambda Functions Are Involved

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



Leave a reply



Submit