1

Using a template type within the parameters of a function passed as argument (either as std::function or as a function pointer) makes the compiler unable to deduce the type when the function is passed as a lambda (see code below). Interestingly this works if the function is not a lambda.

Is there any way to achieve this? I've tried the following code with Visual Studio 2017 and GCC 7.4 and both fail, so it's unlikely to be a compiler bug and probably there is a reason for this behaviour.

#include <iostream>
#include <functional>

template <typename T>
void collide(void (*callback)(T)) // or std::function<void(T)> callback
{
    callback(42);
}

int main() {
    // doesn't compile: "could not deduce template arguments"
    collide([](int a) {
        std::cout << a << std::endl;
    });
}
  • 2
    Does this answer your question? [I cannot pass lambda as std::function](https://stackoverflow.com/questions/36030589/i-cannot-pass-lambda-as-stdfunction) - same goes for function-pointers and lambdas. Though they can be sometimes converted to function pointers, the potential conversion cannot be used with template matching. – Fureeish Jan 15 '20 at 23:23
  • Yes, this seems to be the same case, although my question is a bit more generic since it includes function pointers. What's suggested on that question (casting to std::function then passing) seems to work, although I wonder why the compiler can't do this for me. – Albert Vaca Cintora Jan 15 '20 at 23:31
  • 2
    "*it includes function pointers*" - hence my comment on the function pointers - lambdas are *not* function pointers. They *sometimes can be converted to function pointers*, but the rules are that conversions are not allowed when matching against a `template`. These are the rules. They do make sense and prevent you from accidentally breaking *a lot* of stuff elsewhere, but the drawback is that the compiler cannot, unfortunately, "*do this for you*" right here. – Fureeish Jan 15 '20 at 23:35
  • Didn't know that "conversions are not allowed when matching against a template". That explains it then, thanks :) – Albert Vaca Cintora Jan 16 '20 at 01:11

1 Answers1

3

Try to use the following call

collide( *[](int a) {
    std::cout << a << std::endl;
});

to convert the lambda to a pointer to a function explicitly.

Here is a demonstrative program.

#include <iostream>

template <typename T>
void f( void ( *fn )( T ) )
{
    fn( 42 );
}

void g( int x )
{
    std::cout << "g( " << x << " );\n";
}

int main()
{
    f( g );
    f( *[]( int x ) { std::cout << "main::lambda( " << x << ");\n"; } );
}

Its output is

g( 42 );
main::lambda( 42);
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335