Ben Hollier
Ben Hollier

Reputation: 605

"template argument deduction/substitution failed" error with function object with parameter pack

I'm trying to make a function that takes a variable number of parameters of any type, but even the simple example I made is getting an error

#include <iostream>
#include <functional>

template<class... Ts>
void callFunction(const std::function<void(Ts...)>& function, Ts... parameters)
{
    function(parameters...);
}

void myFunc(const std::string& output)
{
    std::cout << output << std::endl;
}

int main() 
{
    callFunction<const std::string&>(&myFunc, "Hello world");
    return 0;
}

When I run the above code in Ideone, I get this error:

prog.cpp: In function ‘int main()’:
prog.cpp:17:57: error: no matching function for call to ‘callFunction(void (*)(const string&), const char [12])’
  callFunction<const std::string&>(&myFunc, "Hello world");
                                                         ^
prog.cpp:5:6: note: candidate: template<class ... Ts> void callFunction(const std::function<void(Ts ...)>&, Ts ...)
 void callFunction(const std::function<void(Ts...)>& function, Ts... parameters)
      ^~~~~~~~~~~~
prog.cpp:5:6: note:   template argument deduction/substitution failed:
prog.cpp:17:57: note:   mismatched types ‘const std::function<void(Ts ...)>’ and ‘void (*)(const string&) {aka void (*)(const std::__cxx11::basic_string<char>&)}’
  callFunction<const std::string&>(&myFunc, "Hello world");

Upvotes: 1

Views: 869

Answers (1)

max66
max66

Reputation: 66210

A simple suggestion: receive the callable as a deduced typename, not as a std::function

I mean (adding also perfect forwarding)

template <typename F, typename ... Ts>
void callFunction(F const & func, Ts && ... pars)
 { func(std::forward<Ts>(pars)...); }

and, obviously, call it without explicating nothing

callFunction(&myFunc, "Hello world");

This as the additional vantage that avoid the conversion of the callable to a std::function.

Anyway, I see two problems in your code:

1) if you receive the functional as a std::function receiving a list ot arguments types (a variadic list in this case, but isn't important for this problem) as a list of argument of the same types, you have to be sure that the types in the two list match exactly.

This isn't your case because the function receive a std::string const & and you pass as argument a the string literal "Hello world" that is a char const [12] that is a different type.

When the types are to be deduced, this cause a compilation error because the compiler can't choose between the two types.

You could solve receiving two list of types

template <typename ... Ts1, typename Ts2>
void callFunction (std::function<void(Ts1...)> const & function,
                   Ts2 && ... parameters)
 { function(std::forward<Ts2>(parameters)...); }

but now we have the second problem

2) You pass a pointer function (&myFunc) where callFunction() wait for a std::function.

We have a chicken-egg problem because &myFunc can be converted to a std::function but isn't a std::function.

So the compiler can't deduce the Ts... list of types from &myFunc because isn't a std::function and can't convert &myFunc to a std::function because doesn't know the Ts... type list.

I see that you have explicated the first type in the Ts... list, but isn't enough because the Ts... list is a variadic one so the compiler doesn't know that there is only a type in the Ts... list.

A simple solution to this problem is pass the function as a simple deduced F type.

Otherwise, if you have written callFunction() with two templates types lists, you can pass a std::function to the function

std::function<void(std::string const &)>  f{&myFunc};

callFunction(f, "Hello world");

but I don't think is a satisfactory solution.

Upvotes: 2

Related Questions