假设我们有一个继承的抽象Base类:
class Base
{
protected:
Base() {}
virtual ~Base() {}
virtual void on_event_foo(int) {}
virtual void on_event_bar(int) {}
};
struct Concrete : public Base
{
virtual void on_event_foo(int value) {/*do some stuff with @value*/}
};
这是一种了解(在编译时是最好的)来自virtual
的{{1}}函数被覆盖的方法(在构造函数中使用某些代码,还是使用特殊模式)?
我的目的是为使用一些回调的库实现一个包装器;如果我可以检查覆盖功能,我将只创建用户想要的回调。
我希望用户可以选择他想要覆盖的功能。然后在我的代码中,我将仅为重写的函数创建回调。纯Base
函数不是解决方案,因为它们不能在不覆盖所有函数的情况下创建具体类。
在virtual
的构造函数中,目前,我在C API中连接了Base
的 lot 静态回调函数。在这些函数中,我调用相应的成员函数。例如,回调函数是Base
,在static Base::EventFoo(/* ... */)
内调用。这是因为我不能将成员函数作为C库的回调。
但是创建过多的回调会使我的包装更慢。所以,我想只连接用户想要的回调,即知道他所覆盖的功能。
答案 0 :(得分:6)
如果您愿意更改一些内容,可以使用curiously recurring template pattern来确定该功能是否被覆盖
#include <iostream>
template <class Derived>
struct Base
{
virtual void on_event() {}
void raise_event()
{
if (&Derived::on_event == &Base::on_event)
std::cout << "not overridden" << std::endl;
else
std::cout << "overridden" << std::endl;
}
};
struct Concrete1 : Base<Concrete1>
{
virtual void on_event() override {}
};
struct Concrete2 : Base<Concrete2>
{
// no override
};
int main()
{
Concrete1 c1;
Concrete2 c2;
c1.raise_event(); // prints overridden
c2.raise_event(); // prints not overridden
return 0;
}
语句&Derived::on_event == &Base::on_event
应该在编译时解决(如果你担心的话),if
可以被优化掉。
虽然我同意其他人的观点。认为这似乎是一种糟糕的模式。让基类具有像您已有的空事件处理程序会简单得多。
答案 1 :(得分:1)
根本不要使用virtual
方法。如果你想要的只是一些具体的类型Concrete
,根据成员函数的存在将它连接到一堆回调,那么我们可以使用模板。
对于给定的类型和函数名称,我们可以确定编译时是否存在&T::func
。如果是,我们添加回调。所以我们最终得到了很多东西:
template <class T>
void setup_cbs(T& object) {
T* ptr_to_object = ...; // store somewhere
static_if<has_on_event_foo<T>>(
[](auto ptr){
add_event_foo_callback(ptr, [](void* p, int i) {
using U = decltype(ptr);
static_cast<U>(p)->on_event_foo(i);
})
}),
[](auto ){}
)(ptr_to_object);
我假设回调加法器采用指针和回调。你将分别弄清楚如何存储指针,但这似乎更容易。