CRTP 与 GCC 中的去虚拟化标志

CRTP vs devirtualization flag in GCC

我开始研究 CRTP 习语,我注意到 GCC 有一个 fdevirtualize 标志,它应该允许在可能的 vtable 调用时转换为直接调用。

虽然 CRTP 可以应用于任何(C++ 兼容)编译器,但如果我只想使用 gcc 进行开发,我是否可以避免 CRTP 惯用语将去虚拟化过程留给 gcc,或者在可能的情况下使用它总是更好使用静态多态性以避免虚函数调用?

去虚拟化是否有帮助取决于编译器是否内联你的东西。

请注意,CRTP 通常用于实现混合和模板方法模式,例如像这样:

template <typename T, typename Derived>
class pointer_base {
  const Derived& self() const { return static_cast<const Derived&>(*this); }
public:
  T& operator *() const { return *self().get(); }
  T* operator ->() const { return self().get(); }
};

template <typename T>
class smart_pointer : public pointer_base<T, smart_pointer<T>> {
public:
  T* get() const;
};

以及虚调用形式:

template <typename T>
class pointer_base {
protected:
  ~pointer_base() = default;
public:
  virtual T* get() const = 0;
  T& operator *() const { return *get(); }
  T* operator ->() const { return get(); }
};

template <typename T>
class smart_pointer : public pointer_base<T> {
public:
  T* get() const override;
};

两种情况下的用法是

smart_pointer<int> p(new int);
*p = 42;

请注意,可以有多个不同的智能指针 classes。在虚函数版本中,它们都派生自同一个pointer_base。这意味着所有这些都共享一个版本的基本功能。如果是这样的话,去虚拟化就不行了。

只有当相关函数被内联到调用者中时它才会起作用,因为这样编译器就可以将代码专门化为特定的具体智能指针类型。

当然,由于助手很小,它们被内联的可能性很高。另一方面,您现在有一个 pointer_base class 客户会看到并且可能会想使用它。您可以从基类私有派生,但随后必须为要在每个派生 class 中公开的所有成员添加 using 声明。对于像 pointer_base 这样的 classes,它由很多(在我的例子中是 2,但实际上也应该有一个布尔转换)小函数组成,这很烦人。但是,CRTP 的语法混乱也是如此。我认为这归结为品味问题。

当您只有一个大型模板方法时,编译器很可能不会内联它。在那种情况下,它不能去虚拟化。但是 CRTP 有其自身的缺点:无论您是否需要,都会为基的每个实例复制该方法。这会导致代码膨胀。

所以它最终归结为模板与虚拟函数的常见问题:专用代码及其相关代码大小增加,或者虚拟调用的性能损失和它们设置的内联障碍。