我开始研究CRTP成语,并且我注意到GCC有一个fdevirtualize
标志,应该允许在可能的vtable调用直接调用时进行转换。 / p>
虽然CRTP可以应用于任何(符合C ++标准的)编译器,但如果我只想用gcc开发,我可以避免CRTP习惯用法将离开虚拟化过程留给gcc,或者在可能的情况下使用它总是更好吗?为了避免虚函数调用,使用静态多态?
答案 0 :(得分:5)
Devirtualization可能有用与否,具体取决于编译器是否内联您的内容。
请注意,CRTP通常用于实现mixins和模板方法模式,例如像这样:
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;
请注意,可能有多个不同的智能指针类。在虚函数版本中,它们都来自相同的pointer_base。这意味着在所有这些中共享基本函数的一个版本。如果是这种情况,那么虚拟化就无法发挥作用。
只有将相关函数内联到调用者中才会起作用,因为编译器可以将代码专门化为特定的具体智能指针类型。
当然,由于佣工如此之小,他们内联的可能性很高。另一方面,您现在有一个客户端看到并可能想要使用的pointer_base类。您可以从基础私有派生,但是您必须为每个派生类中要公开的所有成员添加声明。对于像pointer_base这样的类,它包含很多(在我的情况下为2,但确实应该有一个布尔转换)的小函数,这很烦人。但是,CRTP的语法混乱也是如此。我想,这归结为品味问题。
当你只有一个大模板方法时,编译器最不会内联它。在这种情况下,它无法虚拟化。但CRTP有其自身的缺点:无论您是否需要,该方法都会在基础的每个实例化中重复。这可能会导致代码膨胀。
因此,它最终归结为模板与虚拟功能的常见问题:专用代码及其相关代码大小增加,或虚拟调用的性能损失以及它们提出的内联障碍。