CRTP vs过载和最终

时间:2013-10-14 15:36:13

标签: c++ c++11 crtp

我经常使用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的优化不能依赖?

2 个答案:

答案 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优化虚函数。

此外,标准说如果已知类名,则可以内联虚拟函数 - 例如没有虚拟调度。