Violet Giraffe
Violet Giraffe

Reputation: 33599

Overloading a function template for a template <typename...> class

I don't understand why this doesn't compile, can you give me a brief rationale?

template <typename... Args>
class Signal{};

template <template <typename... SignalArgs> class Signal, typename... Args>
void f(Signal<SignalArgs...>&& signal, Args&&... args)
{}

The error gcc provides is:

error: 'SignalArgs' was not declared in this scope; did you mean 'Signal'?
   11 | void f(Signal<SignalArgs...>&& signal, Args&&... args)
      |               ^~~~~~~~~~
      |               Signal
<source>:11:25: error: expected parameter pack before '...'
   11 | void f(Signal<SignalArgs...>&& signal, Args&&... args)
      |                         ^~~
<source>:11:28: error: template argument 1 is invalid
   11 | void f(Signal<SignalArgs...>&& signal, Args&&... args)
      |       

And more importantly, what is a proper way to write such an overload? Do note that I need two variants of f - the one in question, and the other is template <typename F> void f(F&&); I definitely don't want any instances of Signal, including Signal<>, going into the generic overload.

Try it online on Godbolt: https://godbolt.org/z/Xawz6G

Upvotes: 0

Views: 85

Answers (1)

Michael Kenzel
Michael Kenzel

Reputation: 15941

The problem is that SignalArgs does not declare a parameter of your function template. It's simply an identifier that may optionally occur in the declarator that declares your actual template parameter, which is Signal. It's basically the same thing as with names of function parameters in a declarator that declares a function type, e.g.:

void f(void(*fp)(int a, int b))
{}

Here, f is a function that takes a parameter fp that is a pointer to a function that takes two parameters of type int. The identifiers a and b are redundant in the same way that SignalArgs is in your original example. They may appear, but they don't really serve any purpose.

Since SignalArgs is not a template parameter of your function template, there's no way it could be deduced (template argument deduction only concerns itself with deducing arguments for the parameters of a function template). The way to do what you want is to introduce a separate template parameter that is used in the type of your function parameter signal in such a way that it can be deduced:

template <template <typename...> class Signal, typename... SignalArgs, typename... Args>
void f(Signal<SignalArgs...>&& signal, Args&&... args)
{}

working example here

Upvotes: 2

Related Questions