【问题标题】:Is there a way to detect if a function is overridden?有没有办法检测函数是否被覆盖?
【发布时间】:2016-10-01 23:20:14
【问题描述】:

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

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

这是一种知道(在编译时最好)被覆盖的Base 中的virtual 函数(在构造函数中使用一些代码,或使用特殊模式)的方法吗?

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

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

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

【问题讨论】:

  • 使基类中的函数成为纯虚函数:virtual void on_event_foo(int) = 0;.
  • @Boiethios 对我来说听起来像是一个设计缺陷。如果派生类是否覆盖了函数,你应该让你的代码完全透明。
  • 我已将“overload”替换为“override”。如果你能给出一个你想要什么的例子,那就太好了。你最后的几句话不清楚。
  • 在编译时通常不知道,因此在编译时您无法检测到它。最多可能要等到链接时间才能知道。 XY 问题。
  • 正式地说,这不是一个 abstract 类。在 C++ 中,一个抽象类至少有一个纯虚函数。

标签: c++ templates inheritance virtual-functions


【解决方案1】:

如果你愿意改变一些东西,你可以使用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;
}

&amp;Derived::on_event == &amp;Base::on_event 语句应该在编译时解决(如果你担心的话),if 可以被优化掉。

虽然我同意其他人的观点,认为这似乎是一种糟糕的模式。让基类拥有像您已经拥有的空事件处理程序会简单得多。

【讨论】:

  • 嗯,我会看到的。这似乎是个好主意:在Base 构造函数中,我检查是否有被覆盖,然后连接回调。
  • 能否根据offsetof进行static_assert?然后你得到一个编译时失败。
  • @Bathsheba 可能,但我不认为这会比我拥有的更好。我最初设想它不需要模板,但显然你不能通过&amp;on_eventthis-&gt;on_event 来获取当前指向的虚拟成员函数
  • @Boiethios 同样,如果您想以抽象方式使用它,您可能需要创建一个基类,模板化的Base 类继承自该类。 (因为所有的Base&lt;...&gt; 类都不一样)
  • 您希望如何使用它?现在所有的混凝土都有不同的基类。
【解决方案2】:

根本不要使用virtual 方法。如果你想要的是一些具体的类型,Concrete,基于成员函数的存在将它连接到一堆回调,那么我们可以使用模板。

对于给定的类型和函数名称,我们可以在编译时确定&amp;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);

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

【讨论】:

  • 这可能是 OPs 问题的更好解决方案。您可能想添加如何实现has_on_event_foo&lt;&gt; 或至少一个链接
  • @vu1p3n0x 这里有很多关于如何做到这一点的问题。
猜你喜欢
  • 2010-11-20
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多