为模板重载函数模板 <typename...> class

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

我不明白为什么这不能编译,你能给我一个简短的理由吗?

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

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

gcc 提供的错误是:

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)
      |       

更重要的是,编写这种重载的正确方法是什么?请注意,我需要 f 的两个变体 - 一个有问题,另一个是 template <typename F> void f(F&&); 我绝对不想要 Signal 的任何实例,包括 Signal<>,进入通用重载。

在 Godbolt 上在线试用:https://godbolt.org/z/Xawz6G

问题是 SignalArgs 没有声明函数模板的参数。它只是一个标识符,可以选择性地出现在 declarator 中,声明您的实际模板参数,即 Signal。它与声明函数类型的声明符中的函数参数名称基本相同,例如:

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

这里,f 是一个带有参数 fp 的函数,它是一个指向带有两个 int 类型参数的函数的指针。标识符 ab 是多余的,就像 SignalArgs 在您的原始示例中一样。它们可能会出现,但实际上并没有任何作用。

由于 SignalArgs 不是您的函数模板的模板参数,因此无法推导它(模板参数推导仅涉及推导函数模板参数的参数)。做你想做的事情的方法是引入一个单独的模板参数,它在你的函数参数类型中使用 signal 以这样一种方式可以推导出它:

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

工作示例here