关于CRTP(Curiously Recurring Template Prattern)的使用

本文探讨了CRTP模式在LightRefBase类中的应用,对比了使用CRTP模式和传统继承方式实现引用计数的优缺点。通过具体代码示例展示了如何利用CRTP模式避免虚函数表带来的额外开销。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在阅读frameworks/rs/cpp/util/RefBase.h之LightRefBase时,我记得《C++设计新思维》里对这种用法是有过介绍的,可是今天翻箱倒柜,怎么都找不到那本奇书了。当年所谓的前卫,今天已经遍地开花,赶紧再把CRTP给复习一下。

CRTP模式主要有两种使用场景:

  • 一、Meyers Counting
template <typename T>
class Counting
{
public:
    Counting() { mCount++; }
    ~Counting() { mCount--; }
    static int mCount;
};

template <typename T> int Counting<T>::mCount = 0;

class CountedA : public Counting<CountedA>{};
class CountedB : public Counting<CountedB>{};

CountedA类和CountedB类都具备了引用计数功能,这是从Counting基类派生下来的,但是引用计数具体的数值是每个子类各自维护,这主要是拜mCount的static属性所赐,Counting<CountedA>和Counting<CountedB>是不同的类,也就有不同的静态成员。

不过LightRefBase显然不是这种应用场景。

  • 二、用编译时绑定替代运行时绑定,避免虚表的时空性能开销。
#include "stdafx.h"
#include <stdio.h>

template <typename T>
class Fucker
{
public:
    void doFuck() { printf("Failed :(\n"); }
    void Fuck() { ((T*)this)->doFuck(); }
};

class AFucker : public Fucker<AFucker>
{};

class BFucker : public Fucker<BFucker>
{
public:
    void doFuck() { printf("Shuang :)\n"); }
};

int main(int argc, char** argv)
{
    Fucker<BFucker>* pFucker = new BFucker();
    pFucker->Fuck();
    return 0;
}

上面,Fuck()和doFuck()共同完成了虚函数的表演,但无需承担虚表的开销。乍一看LightRefBase也不是这种设计,因为它显然没有虚函数的桥段。

 1 template <class T>
 2 class LightRefBase
 3 {
 4 public:
 5     inline LightRefBase() : mCount(0) { }
 6     inline void incStrong(__attribute__((unused)) const void* id) const {
 7         __sync_fetch_and_add(&mCount, 1);
 8     }
 9     inline void decStrong(__attribute__((unused)) const void* id) const {
10         if (__sync_fetch_and_sub(&mCount, 1) == 1) {
11             delete static_cast<const T*>(this);
12         }
13     }
14     //! DEBUGGING ONLY: Get current strong ref count.
15     inline int32_t getStrongCount() const {
16         return mCount;
17     }
18 
19     typedef LightRefBase<T> basetype;
20 
21 protected:
22     inline ~LightRefBase() { }
23 
24 private:
25     friend class ReferenceMover;
26     inline static void moveReferences(void*, void const*, size_t,
27             const ReferenceConverterBase&) { }
28 
29 private:
30     mutable volatile int32_t mCount;
31 };

它唯一使用了模板参数的地方就是#11,可以断定此处就是该设计的初衷。如果用派生的方式,也可以完成这个任务:

 1 class Base
 2 {
 3 public:
 4     Base() :mCount(0) {}
 5     int IncRef() { return ++mCount; }
 6     int DecRef() {
 7         if (--mCount <= 0) {
 8             delete this;
 9         }
10         return mCount;
11     }
12     virtual ~Base() {}
13 private:
14     int mCount;
15 };
16 
17 class Derived : public Base
18 {};

可这样一来就要引入虚表了——有一个析构虚函数。为什么必须要有它呢?因为Base要用于派生。《Effective C++》第14条曰:总让base class拥有virtual destructor,因为经由基类指针删除子类对象时,基类如果没有虚析构函数,结果将是未定义。于是定义虚析构函数,于是引入虚表。LightRefBase对象的开销本来只有一个int32_t那么大,引入虚表,空间开销就double啦,大大得不划算。因此采用了CRTP模式,算是场景二的变种。

转载于:https://www.cnblogs.com/palance/p/5194164.html

CRTPCuriously Recurring Template Pattern,奇异递归模板模式)是一种在C++中常见的模板编程模式,其核心思想是将派生类作为模板参数传递给基类模板。这种模式允许基类在编译时访问派生类的成员,从而实现静态多态性,避免了运行时多态性的开销[^1]。 ### 使用场景 1. **静态多态性** CRTP可以用来实现静态多态性,即在编译时确定调用的函数。这种方式避免了虚函数表的开销,适用于性能敏感的场景。例如,在Clang和LLVM项目中,`RecursiveASTVisitor`和`HeuristicBase`类都使用CRTP来实现高效的静态多态性。 2. **链式调用(Polymorphic Chaining)** CRTP可以用于实现链式调用,使得代码更加简洁和易读。通过返回派生类的引用,可以在一个对象上调用多个方法。例如,下面的`Printer`类模板允许`CoutPrinter`类通过返回`ConcretePrinter`引用来实现链式调用[^2]。 ```cpp template <typename ConcretePrinter> class Printer { public: Printer(std::ostream& pstream) : m_stream(pstream) {} template <typename T> ConcretePrinter& print(T&& t) { m_stream << t; return static_cast<ConcretePrinter&>(*this); } template <typename T> ConcretePrinter& println(T&& t) { m_stream << t << std::endl; return static_cast<ConcretePrinter&>(*this); } private: std::ostream& m_stream; }; class CoutPrinter : public Printer<CoutPrinter> { public: CoutPrinter() : Printer(std::cout) {} CoutPrinter& SetConsoleColor(Color c) { // 设置控制台颜色的实现 return *this; } }; // 使用示例 CoutPrinter().print("Hello ").SetConsoleColor(Color::red).println("Printer!"); ``` 3. **多态拷贝构造(Polymorphic Copy Construction)** CRTP还可以用于实现多态拷贝构造,使得派生类的对象可以在不知道其具体类型的情况下进行拷贝构造。这种方法通常用于需要处理对象克隆的场景[^2]。 ### 示例 - **Clang中的`RecursiveASTVisitor`** 在Clang项目中,`RecursiveASTVisitor`类使用CRTP来遍历抽象语法树(AST)。通过将派生类作为模板参数传递给基类,`RecursiveASTVisitor`能够在编译时访问派生类的成员函数,从而实现高效的遍历。 - **LLVM中的`HeuristicBase`类** LLVM的代码生成模块中使用CRTP的另一个实例`HeuristicBase`类。该类通过CRTP实现了特定的启发式算法,确保了代码的高效性和可维护性。 CRTP是一种强大的模板编程技术,广泛应用于需要高性能和静态多态性的场景。通过合理使用CRTP,开发者可以在编译时优化代码,提高程序的执行效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值