我经常使用CRTP从基类调用派生类函数。它具有不会产生虚拟函数调用成本的好处,例如:
template< class Derived >
class Base
{
public:
void foo()
{
static_cast<Derived*>(this)->foo_impl(); // Non virtual derived class call
}
};
class Derived : public Base<Derived>
{
private:
void foo_impl()
{
// Do Stuff
}
};
C ++ 11引入了final
语言标识符,它将虚函数(或类)标记为final,告诉编译器不会再进一步覆盖该函数。
我的理解(来自Going Native 2013演示文稿)如果虚拟函数被标记为final
,则编译器可以优化代码以消除虚函数调用的开销。
Ergo在上面的示例中,可以删除CRTP并且仍然避免虚函数调用的开销,前提是派生类将虚函数标记为final
,即:
class Base
{
public:
virtual void foo_impl() = 0;
void foo()
{
foo_impl(); // Non virtual derived class call
}
};
class Derived : public Base
{
public:
// function marked as final, no virtual function call overhead
virtual void foo_impl() final
{
// Do Stuff
}
};
如果是这种情况,是否有关于哪种方法最好的建议?
CRTP是否仍然是首选,因为它保证函数调用是非虚拟的,而基于final
的优化不能依赖?
答案 0 :(得分:3)
这取决于你如何传递实例。在一个翻译单元中考虑:
Derived d;
foo( &d );
和另一个:
void foo( Base* b )
{
b->foo();
}
除了LTO之外,编译器没有机会删除虚拟调用开销。
如果是OTOH,你有:
void foo( Derived* d )
{
d->foo();
}
编译器现在可以足够聪明地优化vtable查找,因为它具有所有必要的信息。不过,我认为这不是保证。如果没有最终结果,仍然需要vtable查找,因为d
可能指向从Derived
派生的内容,而foo_impl()
还有另一种{{1}}实现。
答案 1 :(得分:0)
我的5点:
当类名已知时,最近的GCC优化虚函数。
此外,标准说如果已知类名,则可以内联虚拟函数 - 例如没有虚拟调度。