奇异模板模式(Curiously Recurring Template Pattern)

Table of Contents

在 C++ 中,“奇异模板”(Curiously Recurring Template Pattern, 简称 CRTP)是一种常用的模板编程技巧,主要用于实现静态多态(static polymorphism)或为派生类提供通用的功能。它的名字来源于其递归的结构:一个类使用自己作为模板参数传递给基类。

CRTP 的基本结构

CRTP 的核心是定义一个基类模板,并让派生类作为模板参数传递给这个基类。

基本代码示例

#include <iostream>

// 基类模板
template <typename Derived>
class Base {
public:
    void interface() {
        // 调用派生类的方法
        static_cast<Derived*>(this)->implementation();
    }

    // 默认实现(可选)
    void implementation() {
        std::cout << "Base implementation\n";
    }
};

// 派生类
class Derived : public Base<Derived> {
public:
    void implementation() {
        std::cout << "Derived implementation\n";
    }
};

int main() {
    Derived d;
    d.interface();  // 输出 "Derived implementation"
    return 0;
}

关键点

1.  模板参数传递派生类:
•   基类模板 Base 的模板参数是派生类 Derived。
•   在 Base 中,通过将 this 指针静态转换为 Derived* 类型,调用派生类中的方法。

2.  静态多态:
•   在编译期决定调用的是哪一个类的方法,避免了运行时的虚函数开销。
•   不同于动态多态(使用虚函数和 vtable),静态多态在编译期直接展开,性能更高。

3.  递归的奇异性:
•   派生类引用基类模板,并将自身作为模板参数传递,因此称为“奇异”。

使用场景

1. 实现静态多态

CRTP 通常用于替代虚函数的运行时多态,尤其是性能敏感的场景。

代码示例

#include <iostream>

template <typename Derived>
class Shape {
public:
    void draw() {
        static_cast<Derived*>(this)->draw_impl();
    }
};

class Circle : public Shape<Circle> {
public:
    void draw_impl() {
        std::cout << "Drawing Circle\n";
    }
};

class Square : public Shape<Square> {
public:
    void draw_impl() {
        std::cout << "Drawing Square\n";
    }
};

int main() {
    Circle c;
    Square s;
    c.draw(); // 输出 "Drawing Circle"
    s.draw(); // 输出 "Drawing Square"
    return 0;
}

2. 代码复用

CRTP 可以用于基类提供一部分通用功能,而派生类专注于实现特定逻辑。

示例:统计派生类的实例数量

#include <iostream>

template <typename Derived>
class Counter {
private:
    static int count;
public:
    Counter() { ++count; }
    ~Counter() { --count; }

    static int getCount() { return count; }
};

// 静态成员变量定义
template <typename Derived>
int Counter<Derived>::count = 0;

class MyClass : public Counter<MyClass> {};
class AnotherClass : public Counter<AnotherClass> {};

int main() {
    MyClass a, b;
    AnotherClass c;

    std::cout << "MyClass instances: " << MyClass::getCount() << "\n"; // 输出 2
    std::cout << "AnotherClass instances: " << AnotherClass::getCount() << "\n"; // 输出 1

    return 0;
}

3. 控制派生类的行为

基类模板可以对派生类施加一定的约束或统一接口。

优缺点

优点

1.  静态多态:
•   提高性能,无需虚函数和运行时多态的开销。

2.  代码复用:
•   基类模板可以封装通用逻辑,派生类只需专注于具体实现。

3.  灵活性:
•   在基类模板中可以为派生类提供默认实现,也可以强制派生类实现特定行为。

缺点

1.  可读性降低:
•   对新手来说,递归的模板结构可能不容易理解。

2.  类型安全性:
•   如果使用不当(例如 static_cast 错误),可能引发未定义行为。

3.  编译时间开销:
•   模板会增加编译时间,因为 CRTP 是基于模板元编程的。

总结

CRTP 是 C++ 模板编程中的一个强大工具,主要用于静态多态和代码复用。它通过将派生类作为模板参数传递给基类,实现了在编译期确定的多态行为,在高性能场景中尤为常见。

Comments |0|

Legend *) Required fields are marked
**) You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>
Category: 似水流年