Class template: why can't I specialize a single method for void type?

六眼飞鱼酱① 提交于 2020-01-05 08:56:59

问题


I have a class template:

template <typename Argument> class Signal
{
    void invoke(Argument arg) {}
};

Now I want to invoke signals without parameter (meaning void parameter). I assume I could specialize the whole class for void and it would compile. But there's a lot of code in the class, I don't want to duplicate it. I want to only specialize whatever neccessary. So I try adding:

// void specialization
template<> 
void Signal<void>::invoke()
{

}

And get an error:

error C2244: 'Signal::invoke' : unable to match function definition to an existing declaration

Why is that?

The same code works for any type other than void.


回答1:


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.




回答2:


What about C++11 variadic templates :

template<typename ...Args>
struct Signal {

typedef std::function<void(Args...)> slot_type;

std::list<slot_type> listeners;

template<typename Callable>
void connect(Callable callable) {
    listeners.emplace_back(callable);
}

void invoke(Args... args) {
    for(slot_type& slot : listeners)
        slot(args...);
}

};

void show_me(const std::string& data, int id) {
    std::cout << "hello " << data << " : " << id << std::endl;
}

int main() {
    Signal<std::string, int> s;
    s.connect(show_me);
    s.invoke("world", 42);
    // ...
}

It scales well with 0, 1 or more args.




回答3:


If yo will try to declare a variable

void a;

you also will receive a compile error.

The problem is that compiler expects some type instead of Argument here

template <typename Argument> class Signal
{
    void invoke(Argument arg) {}
};

and void is not treated as a type here.



来源:https://stackoverflow.com/questions/21901637/class-template-why-cant-i-specialize-a-single-method-for-void-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!