The specialization for this template:
template <typename Argument> class Signal
{
void invoke(Argument arg) {}
};
would be:
template<>
void Signal<void>::invoke(void arg)
{
}
which is illegal because you can't have a void object.
One way of doing what you want is to use overloading to declare both invoke methods, and use some templating tricks (I believe this one is called SFINAE) to only allow the correct overload available based on your class template argument:
template <typename Argument> class Signal
{
public:
static constexpr bool IsVoid = is_same<Argument, void>::value;
template <typename T = Argument, typename = typename std::enable_if< !IsVoid && is_same<Argument, T>::value>::type >
void invoke(T arg) {
// only available for non-void types
}
template <typename T = Argument, typename = typename std::enable_if< IsVoid >::type >
void invoke() {
// only available for void class specialization
}
}
Signal<void> sv;
Signal<int> si;
sv.invoke(); // OK
si.invoke(1); // OK
sv.invoke(1); // NOT OK
sv.invoke("s"); // NOT OK
si.invoke(); // NOT OK
si.invoke("s"); // NOT OK
You can find more about the usage of enable_if
here: std::enable_if to conditionally compile a member function, Why should I avoid std::enable_if in function signatures.