4

I was trying to write a function to forward arguments for a variadic template function, similar to std::invoke. Here is the code:

#include <functional>

template<class... Args>
void f(Args&&... args) { }

template<template<class...> class F, class... Args>
void invoke(F<Args...> f, Args&&... args) {
    f(std::forward<decltype(args)>(args)...);
}

int main() {
    invoke(f, 1, 2, 3);
    std::invoke(f, 1, 2, 3);
}

However, both my invoke and std::invoke fails to compile. g++ complains that it couldn't deduce template parameter template<class ...> class F. So is it possible to invoke a variadic template function without explicit template specialization?

debug18
  • 143
  • 5
  • a variation about: https://stackoverflow.com/questions/40356990/how-does-template-argument-deduction-work-when-an-overloaded-function-is-involve – papagaga May 04 '18 at 10:03

2 Answers2

5

Note that a function template represents a family of functions. When you, say, pass it to a function it needs to resolve to a certain specialization of the template. I understand what you're trying to do with the parameter of your custom invoke (capture the function template as a template) but sadly this will work only with class templates, never function templates.

You'd need another level of indirection. Namely, passing a functor or lambda that forwards the arguments to f:

invoke([](auto&&... xs) { f(decltype(xs)(xs)...); }, 1, 2, 3);

The difference is that now the argument is a class non-template so it can be deduced by your invoke.

With this change comes the additional requirement that you change your function to fully deduce the first parameter:

template<class F, class... Args>
void invoke(F&& f, Args&&... args) {
  f(forward<Args>(args)...);
}
David G
  • 94,763
  • 41
  • 167
  • 253
  • Nice solution. Is `decltype(xs)` necessary? It works just fine when I removed it. – debug18 May 04 '18 at 12:05
  • 1
    @debug18 That's to do forwarding. You could also spell it `std::forward(xs)` or `static_cast(xs)`, depending on preference. – Barry May 04 '18 at 12:08
2

If function is wrapped inside of template class then you should be able to pass this template class as template template parameter:

#include <functional>

template<class... Args> struct Wrap
{
    static void f(Args&&... args) { }
};

template<template<class...> class F, class... Args>
void invoke(Args&&... args) {
    Wrap<Args...>::f(std::forward<Args>(args)...);
}

int main() {
    invoke<Wrap>(1, 2, 3);
}

online compiler

user7860670
  • 35,849
  • 4
  • 58
  • 84