我有一个课程模板:
template <typename Argument> class Signal
{
void invoke(Argument arg) {}
};
现在我想调用没有参数的信号(意思是void
参数)。我假设我可以专门研究void
的整个类,它会编译。但是课堂上有很多代码,我不想复制它。我想只专注于任何必要的东西。所以我尝试添加:
// void specialization
template<>
void Signal<void>::invoke()
{
}
并收到错误:
错误C2244:'Signal :: invoke':无法匹配功能 对现有声明的定义
为什么?
相同的代码适用于void
以外的任何类型。
答案 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 function,Why 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视为类型。