C++中的CRTP调度11

Rob*_*son 2 c++ templates crtp c++11

说我有以下代码:

template <class Derived>
class Base {
public:
   virtual void foo_impl() = 0;
   void foo() {
      static_cast<Derived*>(this)->foo_impl(); //A
      (*static_cast<Derived*>(this)).foo_impl(); //B
   }
};

class Derived : public Base<Derived> {
private:
   void foo_impl() {
      bar();
   }
};
Run Code Online (Sandbox Code Playgroud)

几个问题:

A行会生成虚函数调用吗?虽然我在互联网上找到的大部分内容都建议以这种方式做事,但对于我来说,我并没有看到编译器如何进行静态调度,因为指向Derived的指针实际上仍然可以指向Derived2类型的对象,其中Derived2:公共派生.

B行是否解决了我之前提到的问题(如果适用)?考虑到现在调用不再是指针而是因此使用*,它似乎会这样.会避免虚函数调用.但是如果编译器将解除引用的强制转换视为引用类型,它仍然可以生成虚函数调用......在这种情况下,解决方法是什么?

将C++ 11 final关键字添加到foo_impl()会改变编译器在(或任何其他相关)情况下的行为方式吗?

And*_*owl 5

A行会生成虚函数调用吗?

是的.foo_impl()是虚拟的并Derived覆盖它.即使foo_impl()in Derived明确标记为virtual,它也在基类中,这足以使其成为虚函数.

B行是否解决了我之前提到的问题(如果适用)?

.无论调用是在指针上还是在引用上都无关紧要:编译器仍然不知道您是foo_impl()在一个派生自 Derived或直接实例的类的实例上调用该函数Derived.因此,通过vtable执行调用.

看看我的意思:

#include <iostream>

using namespace std;

template <class Derived>
class Base {
public:
   virtual void foo_impl() = 0;
   void foo() {
      static_cast<Derived*>(this)->foo_impl();
      (*static_cast<Derived*>(this)).foo_impl();
   }
};

class Derived : public Base<Derived> {
public:
   void foo_impl() {
      cout << "Derived::foo_impl()" << endl;
   }
};

class MoreDerived : public Derived {
public:
   void foo_impl() {
      cout << "MoreDerived::foo_impl()" << endl;
   }
};

int main()
{
    MoreDerived d;
    d.foo(); // Will output "MoreDerived::foo_impl()" twice
}
Run Code Online (Sandbox Code Playgroud)

最后:

是否添加C++ 11 final关键字来foo_impl()改变编译器在(或任何其他相关)情况下的行为方式?

从理论上讲,是的.该final关键字将无法在子类中覆盖该函数Derived.因此,当foo_impl()通过指针执行函数调用时Derived,编译器可以静态解析调用.但是,据我所知,C++标准不要求编译器这样做.

结论:

无论如何,我相信你真正想要做的就是不要foo_impl()在基类中声明这个函数.使用CRTP时通常就是这种情况.此外,您必须声明类Base<Derived>friendDerived,如果你想要访问Derivedprivate功能foo_impl().否则,你可以foo_impl()公开.

  • 我认为当函数声明为`final`时,编译器可能有足够的信息来进行静态调用而不是虚拟调用,作为优化.不知道有多大可能. (2认同)
  • @RobertMason:你通过让函数调用`Base`类中的`foo_impl()`来自动强制执行.如果您对错误消息的冗长感到困扰,那就是另一个故事.我不会因为错误信息难以理解而大幅改变软件的设计.如你所说,切换到另一个编译器.或者,如果你不能这样做,请使用[概念式SFINAE技术](http://stackoverflow.com/questions/87372/check-if-a-class-has-a-member-function-of-a-given-签名)和`static_assert()`来检查这些方法的存在. (2认同)
  • @RobertMason:另请注意,阅读错误消息(包括来自模板的消息)是一种技巧,您需要对其进行训练. (2认同)