Search code examples
c++templatesc++17std-functiontemplate-argument-deduction

std::function template param can not be deduced


I'm working on code like following one

#include <functional>

template <typename Type>
void foo(const std::function<void(const Type&)> & handler) {}

void goo (const int&){}

int main() {
    foo([](const int&){});
    foo(goo);
}

unfortunate it refuses to compile on (clang 6.0.0 and gcc 8.1.1) due to following error

candidate template ignored: could not match 'function<void (const type-parameter-0-0 &)>' against '(lambda at test3.cpp:13:9)'
candidate template ignored: could not match 'function<void (const type-parameter-0-0 &)>' against '(lambda at test3.cpp:13:9)'

Is it possible to somehow force it to deduce Type correctly?


Solution

  • You tagged C++17, so you can use deduction guides for std::function's.

    You can try something as follows

    template <typename F,
              typename Type = typename decltype(std::function{std::declval<F>()})::argument_type>
    void foo (F f)
     {
     }
    

    I know that argument_type is deprecated in C++17, but you can substitute it with a simple custom template.

    By example

    template <typename>
    struct firstArg;
    
    template <typename R, typename A0, typename ... As>
    struct firstArg<std::function<R(A0, As...)>>
     { using type = A0; };
    

    and foo() can be written as

    template <typename F,
              typename FUNC = decltype(std::function{std::declval<F>()}),
              typename Type = typename firstArg<FUNC>::type>
    void foo (F f)
     {
     }
    

    This way the callable f isn't a std::function but it's original type (and this can be better or worse, depending from your exact requirements); if you need it in a std::function, you can obtain it inside the foo() function using again deduction guides or the FUNC type

    template <typename F,
              typename FUNC = decltype(std::function{std::declval<F>()}),
              typename Type = typename firstArg<FUNC>::type>
    void foo (F f)
     {
       FUNC fnc{f};
     }