Avoid to specity template argument when using a function

93 views Asked by At

I want to write a template function that take a std::function as argument, something like:

 template<typename T>
 void registerDelegate(std::function<void(const T&)> d) {
   d(T{});
 }

When I want to use it, I need to write in an explicit way the T template argument:

 auto d = [](const std::string& message) { int i = 0; };
 registerDelegate<std::string>(d);

that's annoying since I've already defined it in the lambda. Is there a way to avoid it and simply write:

registerDelegate(d);

When I want to use the function, while always knowing what T is?

3

There are 3 answers

4
Ted Lyngmo On BEST ANSWER

You could forward whatever functor that is supplied to registerDelegate to a lambda (or function template ) taking a std::function<void(const T&> to deduce T.

template <class F>
void registerDelegate(F&& d) {
    []<class T>(std::function<void(const T&)> del) {
        del(T{});
    }(std::function(std::forward<F>(d))); // use std::function's deduction guides
}

Instead of changing your current function template which already matches on std::function<void(const T&)>, you could add a function template for functors/functions not already wrapped up in a std::function:

template<class F>
decltype(auto) registerDelegate(F&& func) {
    // call your already existing function:
    return registerDelegate(std::function(std::forward<F>(func)));
    //                           ^ use std::function's deduction guides
}
0
YSC On

You can leverage std::function's deduction guides:

template<class Functor>
void registerDelegate(Functor&& functor)
{
   std::function f = functor;
   (void) f;
}

If you need to access the argument type:

template<class Functor>
auto to_function(Functor&& functor)
{
    std::function f = functor;
    return f;
}
 
template<class Arg>
void registerDelegate(std::function<void(const Arg&)> d) {
    d(Arg{});
}

auto d = [](std::string const&){};
registerDelegate(to_function(d));
0
Jan Schultke On

The problem is that implicit conversions are not considered during template argument deduction. If you want to deduce T in registerDelegate, you need a std::function, not just something convertible to std::function.

In C++17, this is easily solved by using class template argument deduction, and the std::function deduction guide.

auto d = [](std::string const&){};
registerDelegate(std::function{d});

See live example at Compiler Explorer.