类模板:为什么我不能专门为void类型设置单个方法?

时间:2014-02-20 08:01:25

标签: c++ template-specialization

我有一个课程模板:

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

现在我想调用没有参数的信号(意思是void参数)。我假设我可以专门研究void的整个类,它会编译。但是课堂上有很多代码,我不想复制它。我想只专注于任何必要的东西。所以我尝试添加:

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

}

并收到错误:

  

错误C2244:'Signal :: invoke':无法匹配功能   对现有声明的定义

为什么?

相同的代码适用于void以外的任何类型。

3 个答案:

答案 0 :(得分:1)

此模板的专业化:

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

将是:

template<> 
void Signal<void>::invoke(void arg)
{

}

这是非法的,因为你不能拥有一个虚空对象。

做你想做的事情的一种方法是使用重载来声明两个调用方法,并使用一些模板技巧(我相信这个被称为SFINAE)只允许基于你的类模板提供正确的重载参数:

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

您可以在此处找到有关enable_if使用情况的更多信息:std::enable_if to conditionally compile a member functionWhy should I avoid std::enable_if in function signatures

答案 1 :(得分:1)

C ++ 11可变参数模板怎么样:

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);
    // ...
}

它可以很好地扩展0,1或更多args。

答案 2 :(得分:0)

如果哟将尝试声明变量

void a;

你也会收到编译错误。

问题是编译器需要某种类型而不是Argument

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

并且此处不将void视为类型。