优化从基类实现内部对派生类虚拟方法的调用

问题描述 投票:0回答:1

如果已经有人问过这个问题,请提前道歉,因为您可能可以从标题中看出,我不太确定如何以可搜索的方式表达这个问题。

假设我有一个抽象类和派生类,如下所示:

class base {
public:
    virtual void pure_func() = 0;
    virtual void virt_func() {
        // virtual call
        pure_func();
    }
};

class derived : public base {
public:
    // final method
    void pure_func() final {
        // ... do something ...
    }
};

我想知道是否有一种方法可以继承

virt_func
的实现,以便对
pure_func
的调用是非虚拟的。我知道,如果类型是静态已知的,常见的编译器优化是跳过 vtable 查找,但由于此实现是在基类中编写一次,我相信这需要虚拟调用。我或多或少地问是否有一种方法可以为每个派生类重新实现抽象基类方法,以便优化编译器可以更好地处理虚拟调用(例如,跳过虚函数表查找)。我从未听说过有这样的优化。

我曾经想过但由于各种原因而决定放弃的事情:

  1. 使用“标准”定义编写宏(可读性问题+防止使用受保护/私有基础成员);
  2. 将实现复制粘贴到每个派生类中(可维护性+防止使用受保护/私有基成员)
  3. 编写一个我从中扩展的“实现类”(只是将问题推进一步);
  4. 静态多态性/静态调度(据我所知,这排除了运行时多态性的好处,因为没有一个派生类型具有公共基类型)。
  5. 组合(专门与实现一起编写一个共享的单独类型)——我的直觉表明这可以工作,但可能会出现与宏和静态多态性情况类似的问题。

我问这个主要是出于教育目的。然而,当我为使用遵循此模式的函数的基本缓冲区类编写一些热代码时,问题就出现了,因此我希望尽可能避免不必要的虚拟调用。需要注意的是,在我的具体情况下,基类中所有实现的函数都相对较小,只是数量众多。

c++ inheritance vtable
1个回答
0
投票

尽管我有关于可搜索性的观点,但在问完这个问题后不久,我还是设法找到了 以下 StackOverflow 问答。为了汇编程序的简洁性并解决我在问题中指出的问题,我稍微修改了代码。最后,解决方案实际上是我问题中的想法 3 和 4 的结合:

struct base {
    virtual int once() = 0;
    virtual int shared() = 0;
};

template <typename T>
struct master : base {
    virtual int shared() override {
        auto ptr = static_cast<T*>(this);
        return ptr->once();
    }
};

struct derived1 final : public master<derived1> {
    int once() final { return 1; }
};

struct derived2 final : public master<derived2> {
    int once() final { return 2; }
};

这是编译器资源管理器输出的链接。在链接的代码中,我使用

noinline
指令来说明直接函数调用而不是 vtable 查找,因为 clang 内联了大多数函数调用。

汇编输出显示(至少使用 clang),派生类方法是通过各自的

master<derived1>::shared()
master<derived2>::shared()
方法内部的静态分派直接调用的。

这解决了以下痛点:

  1. 它允许共享基类的运行时多态性,
  2. 它使用模板来允许“共享”实现,这些实现也可以在派生类型上进行优化。
© www.soinside.com 2019 - 2024. All rights reserved.