用模板实现虚函数的覆盖机制

我最近想到了在没有虚拟表的情况下实现虚拟函数或使用 CRTP 存储指针(尽管static_cast<CRTP&>(*this)改为使用。

与传统的虚拟功能相比,初始设置相当麻烦。

所以代码是:

namespace detail
{
    template<typename T, typename = void>
    struct virtual_set_up
    {
        void operator()(T &) {}
    };

    template<typename T>
    struct virtual_set_up<T, std::void_t<decltype(std::declval<T>().set_up())>>
    {
        void operator()(T &t) { t.set_up(); }
    };
}

template<typename CRTP>
class base
{
public:
    base() {}

    void set_up() { detail::virtual_set_up<CRTP>()(static_cast<CRTP &>(*this)); }

protected:
    ~base() = default;
};

class settable : public base<settable>
{
public:
    void set_up() { std::cout << "settable: set_up overridden" << std::endl; }
};

class dummy : public base<dummy>
{
public:
};

int main(int, char **)
{
    settable s;
    dummy d;

    base<settable>& baseS = s;
    base<dummy>& baseD = d;
    baseS.set_up();
    baseD.set_up();

    return 0;
}

但是有一个问题:virtual_set_up<dummy>解决了Twith 声明T::set_up的特殊化导致执行时的 SEGFAULT。发生这种情况是因为 dummy 是从 base 公开继承的,它确实有一个set_up方法。

鉴于前面的问题是可以解决的,与传统的虚函数相比,这是否会增加任何效率?

回答

为了解决你的无限递归,你可能仍然比较&dummy::setup!= &base<dummy>::setup

namespace detail
{
    template <typename B, typename T, typename = void>
    struct virtual_set_up
    {
        void operator()(T&) {}
    };

    template <typename B, typename T>
    struct virtual_set_up<B, T,
               std::enable_if_t<!std::is_same_v<decltype(&B::set_up),
                                                decltype(&T::set_up)>>>
    {
        void operator()(T& t) { t.set_up(); }
    };
}

template <typename CRTP>
class base
{
public:
    base() {}

    void set_up() { detail::virtual_set_up<base, CRTP>()(static_cast<CRTP &>(*this)); }

protected:
    ~base() = default;
};

演示

但更简单的是重命名/拆分一个 base<CRTP>

template <typename CRTP>
class base
{
public:
    base() {}

    void set_up() { static_cast<CRTP &>(*this).set_up_v(); }

    void set_up_v() { std::cout << "basen"; }

protected:
    ~base() = default;
};

class settable : public base<settable>
{
public:
    void set_up_v() { std::cout << "settable: set_up overridden" << std::endl; }
};

演示

与传统的虚函数相比,这是否增加了任何效率?

所有代码都在编译时解析,没有动态调度,所以没有虚拟调度的开销......

但是这里没有什么是多态的:base<dummy>并且base<settable>是不相关的类(您不必将std::vector<base>它们存储在一起)。所以比较是不公平的。

对于在编译时已知所有类型的情况,编译器可能会使用去虚拟化优化并消除虚拟调用的开销。


以上是用模板实现虚函数的覆盖机制的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>