1

We have a function that takes a function object as parameter. There are two overloads of the function, which differ in the function signature.

#include <functional>

template <typename T>
void foo(std::function<void(T)> bar)
{

}

template <typename T>
void foo(std::function<void(int, T)> bar)
{

}

int main()
{
    foo([](float number) {

    });
    return 0;
}

However, this code doesn't compile.

error C2784: 'void foo(std::function<void(int,T)>)' : could not deduce template argument for 'std::function<void(int,T)>' from 'main::<lambda_2b4e4413ec419a4ac179a0b64ebde221>' : see declaration of 'foo'
error C2784: 'void foo(std::function<void(T)>)' : could not deduce template argument for 'std::function<void(T)>' from 'main::<lambda_2b4e4413ec419a4ac179a0b64ebde221>' : see declaration of 'foo'

I think there is a problem with both the template itself and the overload. How can I provide two functions that match the above signatures?

danijar
  • 32,406
  • 45
  • 166
  • 297
  • lambda is not derived from std::function – 4pie0 Sep 06 '14 at 13:34
  • 1
    Template type deduction tries to find types for `T`, such that `std::function`, or `std::function` respectively, becomes *equal* to the type of the argument `[](float){}`. There is no such `T` that makes the type of the lambda equal to some `std::function<..>` type. – dyp Sep 06 '14 at 13:36

2 Answers2

3

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){} } );
4pie0
  • 29,204
  • 9
  • 82
  • 118
  • 1
    The `static_cast`, btw, can even be left out: `foo( std::function{ [](float){} } );` but that doesn't answer the OP's question, I guess. – dyp Sep 06 '14 at 13:49
  • well, once it is true it is still better to use static_cast to signal the harshness of cast – 4pie0 Sep 06 '14 at 17:32
1

The reason is apparently one of the side effects of having each lambda expression to be have its own type.

This means for example that you cannot declare a lambda parameter, or a pointer to a lambda. It also means that you cannot use lambdas when type deduction is needed (e.g. for template instantiation).

The solution is to wrap lambdas into std::function<...> objects as soon as possible because those have a type that can be specified and matched in templates.

For example you cannot have a function that returns a lambda, or store a lambda in a member... but you can return an std::function or store std::function into members.

In the specific your code works with

foo(std::function<void(float)>([](float number){});
6502
  • 112,025
  • 15
  • 165
  • 265
  • 1
    *"It also means that you cannot use lambdas when type deduction is needed"* Too general. You cannot deduce the return type of a lambda directly via "pattern matching" etc. You can do what the OP wants via SFINAE. *"For example you cannot have a function that returns a lambda"* We have C++14 now, with return type deduction for ordinary functions. *"or store a lambda in a member."* This is always possible by deducing the type of the lambda, and using a class template. What you *cannot* do, (IMHO to be more precise), is *name* the type of the lambda directly. – dyp Sep 06 '14 at 20:58