有没有办法检测函数是否被覆盖?

Is there a way to detect if a function is overridden?

假设我们有一个继承的抽象基class:

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*/}
};

是否有一种方法可以知道(在编译时最好)来自 Basevirtual 函数被覆盖(使用构造函数中的某些代码,或使用特殊模式)?

我的目的是为使用一些回调的库实现一个包装器;如果我可以检查覆盖函数,我将只创建用户想要的回调。

我希望用户可以选择他想要覆盖的功能。然后在我的代码中,我将仅为被覆盖的函数创建回调。纯 virtual 函数不是解决方案,因为它们不能允许在不覆盖所有函数的情况下创建具体的 class。

Base的构造函数中,现在,我在C API中连接了很多Base的静态回调函数.在那些函数中,我调用相应的成员函数。比如回调函数是static Base::EventFoo(/* ... */),在object->on_event_foo(/* .. */)里面调用。这是因为我无法将成员函数作为 C 库的回调。 但是创建太多回调会使我的包装器变慢。所以,我只想连接用户想要的回调,即知道那里的功能被他覆盖了。

如果你愿意改变一些东西,你可以使用 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 可以被优化掉。

虽然我同意其他人的意见,这似乎是一个糟糕的模式。让基础 class 像您已有的那样具有空事件处理程序会简单得多。

根本不要使用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);

我假设回调加法器接受一个指针和一个回调。您将不得不单独弄清楚如何存储指针,但这似乎更容易。