好的,所以我想我也要尝试吃蛋糕。我不认为这是可能的,但我们走了。
我正在处理一个简单的事件系统,其中回调是在某个用户定义的侦听器类中定义的,而事件发射器将该类作为模板参数。获取模板arg的原因是我想避免使用虚拟调度来调用侦听器方法。
// Class in charge of emitting events.
template<typename TListener = DefaultEmptyListener>
class Emitter {
public:
Emitter(TListener& l) : mListen(l) {}
EmitFoo(...) {
mListen.OnFoo(...);
}
EmitBar(...) {
mListen.OnBar(...);
}
private:
TListener& mListen;
};
class SomeUserDefinedListener() {
void OnFoo() {} // Not virtual
// OnBar(); --> Not defined. Want default implementation.
}
我的问题是,我希望避免让用户定义“开启*”。所有可能事件的方法。也就是说,如果用户没有提供方法,我希望监听器类具有事件方法的默认实现。
有没有办法在没有引入虚拟调度的情况下为侦听器提供默认实现?
答案 0 :(得分:1)
当您处理编译时确切类型已知的对象(即没有动态调度)时,您不需要virtual
。只需创建一个基类,其中包含所有处理程序方法的默认实现,以及一个覆盖要自定义的处理程序的派生类,并将派生类作为模板参数传递。
如果您希望能够将 base 类作为TListener
模板参数传递,但是让mListen
变量实际引用派生类的实例,那么您需要将处理程序函数声明为virtual
。但如果TListener
始终是mListen
引用的对象的确切类型,则您不需要virtual
。
(BTW,&#34;虚拟继承&#34;与普通虚拟功能不同,我认为这就是你所指的。)
答案 1 :(得分:1)
您可以使用没有虚拟多态的继承:
// Class in charge of emitting events.
template<typename TListener = DefaultEmptyListener>
class Emitter {
public:
Emitter(TListener& l) : mListen(l) {}
EmitFoo(...) {
mListen.OnFoo(...);
}
EmitBar(...) {
mListen.OnBar(...);
}
private:
TListener& mListen;
};
class SomeUserDefinedListener() : public DefaultEmptyListener {
void OnFoo() {} // Not virtual
// OnBar(); --> Not defined. DefaultEmptyListener implementation.
}
答案 2 :(得分:-1)
您可以在C ++ 17中轻松完成,使用if constexpr
和detection idiom(其中ns::is_detected
为std::experimental::is_detected
或类似)
// Class in charge of emitting events.
template<typename TListener = DefaultEmptyListener>
class Emitter {
public:
Emitter(TListener& l) : mListen(l) {}
EmitFoo(...) {
if constexpr (ns::is_detected_v<decltype(mListen.OnFoo(...)>>)
{ mListen.OnFoo(...); }
else
{ /* Default empty behaviour */ }
}
EmitBar(...) {
if constexpr (ns::is_detected_v<decltype(mListen.OnBar(...)>>)
{ mListen.OnBar(...); }
else
{ /* Default empty behaviour */ }
}
private:
TListener& mListen;
};
可以使用常规SFINAE方法将其反向移植到C ++ 14 / C ++ 11。