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



我最近想到了在没有虚拟表的情况下实现虚拟函数,或者用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>解决了T的特殊化,声明的T::set_up在执行时导致SEGFAULT。之所以会发生这种情况,是因为dummy是从base公开继承的,而base确实有一个set_up方法。

考虑到前面的问题是可以解决的,这是否比传统的虚拟函数增加了任何效率?

要解决无限递归,您仍然可以将"&dummy::setup!=CCD_ 7">

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>)。所以比较是不公平的。

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

最新更新