出于性能原因,我使用 CRTP,因此我有以下层次结构:
template <typename Derived>
class Base{
public:
double do_sthing(){
static_cast<Derived*>(this)->do_sthing();
}
};
class DerivedA : public Base<DerivedA>{
public:
inline double do_sthing(){ ... }
};
class DerivedB : public Base<DerivedB>{
public:
inline double do_sthing(){ ... }
};
但现在我想将 DerivedA
和 DerivedB
类型的对象放入 vector 中并执行以下代码:
for(auto obj : my_vector) {
obj->do_sthing();
}
我的解决方案是创建一个通用接口(interface)类,Base
将从中继承:
class Interface {
virtual void do_sthing() = 0;
};
但在这种情况下,我会产生虚拟调用开销,并且我执行了所有 CRTP 来避免它。有没有办法避免这种情况?
我想将 Base
中的方法 do_sthing
声明为 Final。
template <typename Derived>
class Base{
public:
double do_sthing() override final{
static_cast<Derived*>(this)->do_sthing();
}
};
我会承受上述虚拟通话费用吗?
事实上,如果我使用虚拟方法和普通的旧多态性,我的性能损失不仅来自 vtable 查找,而且还来自模板情况下丢失内联
最佳答案
当您将不同的类组合到一个仅在运行时才知道的公共(public)结构(如此处的 std::vector)时,您无法避免虚拟调用开销。这是一般的 C++ 原则,例如在 std::function、std::any 等中也可以找到。 CRTP 在这方面没有什么不同。
此外,CRTP(也称为静态多态性)主要是一种避免代码重复的工具(没有任何性能开销)。它并不是为了加速虚拟函数调用,因为那是动态多态性的东西。
除此之外,您基本上有两种方法来设置您的类:
一种方法是使用三重继承层次结构,我发现通过不同的函数名称分隔动态和静态继承很有用。示例:
struct Interface
{
virtual ~Interface() = default;
virtual double do_sthing_impl() = 0;
auto do_sthing() { return this->do_sthing_impl(); }
};
template<typename Derived>
struct Base<Derived> : public Interface
{
virtual double do_sthing_impl() const override { return this->do_sthing(); }
auto do_sthing() { return static_cast<Derived&>(*this).do_sthing(); }
};
struct DerivedA : public Base<DerivedA>
{
auto do_sthing() { /* ... */ }
};
然后:
std::vector<std::unique_ptr<Interface> > v;
v.push_back(std::make_unique<DerivedA>());
v.push_back(std::make_unique<DerivedB>());
第二种选择是type_erasure,例如通过以下代码(基本上是发明了一个继承结构,但没有紧密耦合)
template<typename Derived>
struct Base{ /* ... */ };
struct DerivedA : public Base<DerivedA>{ /* ... */ };
struct type_erased
{
virtual double do_sthing() = 0;
};
template<typename Derived>
struct type_erased_impl : public type_erased
{
Derived d;
type_erasure_impl(Derived d) : d(std::move(d)) {}
virtual double do_sthing() const override { return d.do_sthing(); }
};
然后:
std::vector<std::unique_ptr<type_erased> > v;
v.push_back(std::make_unique<type_erased_impl<DerivedA> >(DerivedA{}));
v.push_back(std::make_unique<type_erased_impl<DerivedB> >(DerivedB{}));
我更喜欢第二种选择,因为它更灵活并且不引入固定的继承结构。相反,您可以实现同一类的多个类型删除版本,根据实际需要,仅公开特定的函数。
关于c++ - 将 crtp 与公共(public)接口(interface)类一起使用时,如何避免虚拟调用开销?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60975375/