Search code examples
c++templateslambdatype-inferenceapple-clang

Why can't the template type be inferred in this C++ function template?


Given the two function templates below:

template <typename T>
void gorp(T*, std::function<void(T*)>)
{
}

template <typename T>
void klop(T*, std::function<void()>)
{
}

The function klop() works as I expect, but gorp() does not:

int x = 3;

//fails: candidate template ignored: could not match 
//'function<void (type-parameter-0-0 *)>' against '(lambda at ...)
gorp( &x, [](int*){}); //1

//works
gorp<int>( &x, [](int*){}); //2

//fails: candidate template ignored: could not match 
//'function<void (type-parameter-0-0 *)>' against 'void (^)(int *)'
gorp( &x, ^(int*){}); //3

//works
gorp<int>( &x, ^(int*){}); //4

//works
klop( &x, [](){}); //5

//works
klop( &x, ^(){}); //6

Lines 3,4,6 use clang blocks; lines 1,2,5 use lambdas.

Notice the calls to klop() have no problem inferring T, but I have to help out the calls to gorp().

I'm at a loss. Shouldn't it be doubly easy for gorp() to figure out T?


Solution

  • It won't infer the type T because you're not passing a std::function. The type inference doesn't work if a conversion is also required. This does work:

    gorp(&x, std::function<void(int*)>([](int*) {}));
    

    And so does this, using plain function pointers:

    template <typename T>
    void gorp(T*, void(*)(T*))
    {
    }
    
    gorp(&x, +[](int*) {}); // + converts captureless lambda to function pointer
    

    Or to support Clang "blocks":

    template <typename T>
    void gorp(T*, void(^)(T*))
    {
    }
    

    And finally, to support all of the above with a single definition:

    template <typename T, typename F>
    void gorp(T*, F&& f)
    {
    }
    

    This can be more efficient than a definition taking std::function if the function is called many times, as std::function does carry some overhead.