2015-04-27 50 views
2

我开始研究CRTP成语,我注意到GCC有一个fdevirtualize标志,它应该允许在可能的vtable调用转换为直接调用时进行转换。虽然CRTP可以与任何(C++兼容)编译器一起使用,但是如果我只想使用gcc进行开发,是否可以避免使用CRTP习惯用法离开gcc进行虚拟化过程,或者在可能的情况下最好使用它,以便使用静态多态性以避免虚函数调用?CRTP vs GCC中的虚拟化标志

回答

4

根据编译器是否嵌入你的东西,Devirtualization可能有用或不用。

请注意,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类。你可以从基地私人派生,但是你必须为每个派生类中要公开的所有成员添加声明。像pointers_base这样的类包含很多(我的情况是2,但实际上应该有一个布尔转换)小函数,这很烦人。但是,CRTP的语法混乱也是如此。我认为,这归结为一个品味问题。

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

所以最终归结为模板VS虚函数通常的问题:与其相关的代码尺寸的增加,或虚拟调用的性能损失,他们竖起了内联屏障的专业代码。

+0

很好的解释,谢谢。 – Jepessen