没有虚拟分派的默认方法实现

时间:2018-01-23 02:54:42

标签: c++

好的,所以我想我也要尝试吃蛋糕。我不认为这是可能的,但我们走了。

我正在处理一个简单的事件系统,其中回调是在某个用户定义的侦听器类中定义的,而事件发射器将该类作为模板参数。获取模板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.
}

我的问题是,我希望避免让用户定义“开启*”。所有可能事件的方法。也就是说,如果用户没有提供方法,我希望监听器类具有事件方法的默认实现。

有没有办法在没有引入虚拟调度的情况下为侦听器提供默认实现?

3 个答案:

答案 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 constexprdetection idiom(其中ns::is_detectedstd::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。