我开始研究 CRTP 习语,我注意到 GCC 有一个 fdevirtualize
标志,它应该允许在可能的情况下将 vtable 调用转换为直接调用。
虽然 CRTP 可以应用于任何(符合 C++ 的)编译器,但如果我只想用 gcc 进行开发,我可以避免 CRTP 习惯用法将去虚拟化过程留给 gcc,或者在可能的情况下使用它总是更好,以便在为了避免虚函数调用?
最佳答案
去虚拟化可能有帮助,也可能无济于事,这取决于编译器是否内联你的东西。
请注意,CRTP 通常用于实现 mixin 和模板方法模式,例如像这样:
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 类,客户可以看到并且可能会想使用它。您可以从基类私有(private)派生,但随后必须为要在每个派生类中公开的所有成员添加 using 声明。对于像pointer_base这样的类,它由很多小函数组成(在我的例子中是2个,但实际上也应该有一个 bool 转换),这很烦人。但是,CRTP 的语法困惑也是如此。我认为这归结为品味问题。
当您只有一个大模板方法时,编译器很可能不会内联它。在这种情况下,它不能去虚拟化。但是 CRTP 有其自身的缺点:无论您是否愿意,该方法都会为基础的每个实例化而重复。这会导致代码膨胀。
因此,最终归结为模板与虚函数的常见问题:具有相关代码大小增加的专用代码,或者虚调用的性能损失和它们设置的内联障碍。
关于c++ - GCC 中的 CRTP 与去虚拟化标志,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/29890393/