跳转至

嵌入式现代C++教程——模板与继承:CRTP与静态多态

引言:当模板遇见继承

模板和继承是C++两大核心特性,但它们通常被视为彼此独立的工具——模板用于泛型编程,继承用于面向对象设计。然而,当这两者结合时,会产生一些强大而优雅的模式。

其中最著名的就是奇异递归模板模式(Curiously Recurring Template Pattern,CRTP)。这个名字听起来很奇怪,但它的应用场景非常广泛:从单例模式到对象计数,从多态复制到接口注入。

本章我们将深入探讨CRTP的原理与应用,对比静态多态与动态多态的优劣,并学习如何使用Mixin模式为类添加功能。


CRTP:奇异递归模板模式

什么是CRTP?

CRTP是一种C++惯用法,其核心思想是:派生类将自己作为模板参数传递给基类

template<typename Derived>
class Base {
public:
    void interface() {
        // 基类通过转型调用派生类的实现
        static_cast<Derived*>(this)->implementation();
    }
};

class Derived : public Base<Derived> {
public:
    void implementation() {
        // 派生类的具体实现
    }
};

这个模式看起来"奇异"是因为:

  1. 派生类继承自一个以自己为模板参数的基类
  2. 基类通过static_cast<Derived*>(this)访问派生类的成员

为什么需要CRTP?

CRTP解决了三个核心问题:

  1. 静态多态:在不使用虚函数的情况下实现多态行为
  2. 代码复用:在基类中实现通用逻辑,调用派生类的具体实现
  3. 编译期类型检查:确保派生类实现了所需的接口

让我们通过一个嵌入式场景来理解——设备驱动框架:

// 设备基类(CRTP)
template<typename Derived>
class DeviceBase {
public:
    // 通用初始化流程
    void initialize() {
        // 1. 硬件复位
        static_cast<Derived*>(this)->reset_hardware();

        // 2. 配置寄存器
        static_cast<Derived*>(this)->configure_registers();

        // 3. 校准
        static_cast<Derived*>(this)->calibrate();

        // 4. 通用后处理
        static_cast<Derived*>(this)->set_initialized();
    }

    // 通用读取流程
    auto read() {
        static_cast<Derived*>(this)->start_conversion();
        while (!static_cast<Derived*>(this)->is_ready()) {
            // 等待转换完成
        }
        return static_cast<Derived*>(this)->read_value();
    }
};

// ADC设备实现
class ADCDevice : public DeviceBase<ADCDevice> {
public:
    void reset_hardware() {
        // ADC特定的复位逻辑
    }

    void configure_registers() {
        // ADC特定的配置
    }

    void calibrate() {
        // ADC特定的校准
    }

    void set_initialized() {
        // 标记初始化完成
    }

    void start_conversion() {
        // 启动ADC转换
    }

    bool is_ready() {
        // 检查转换是否完成
        return true;
    }

    uint16_t read_value() {
        // 读取ADC值
        return 0;
    }
};

// 使用
ADCDevice adc;
adc.initialize();
uint16_t value = adc.read();

关键点

  • 所有设备共享相同的初始化和读取流程
  • 每个设备提供自己的具体实现
  • 没有虚函数调用,所有调用都可以内联
  • 编译期保证类型安全

CRTP的本质:静态多态

CRTP实现的是静态多态(编译期多态),与虚函数实现的动态多态(运行时多态)形成对比:

特性 动态多态(虚函数) 静态多态(CRTP)
绑定时机 运行时 编译期
性能开销 虚表查找 + 间接调用 零开销(可内联)
内存开销 每个对象一个vptr 无额外内存
类型检查 运行时(通过虚表) 编译期
代码大小 较小(一份函数实现) 较大(每个类型一份)
二进制兼容性 稳定(ABI兼容) 不稳定(模板实例化)
可扩展性 运行时可添加新类型 编译期确定

CRTP的工作原理

类型转换详解

CRTP的核心在于static_cast<Derived*>(this)

template<typename Derived>
class Base {
public:
    void method() {
        Derived* d = static_cast<Derived*>(this);
        d->impl();  // 调用派生类方法
    }
};

为什么这样是安全的?

Derived继承自Base<Derived>时:

  1. Base<Derived>this指针实际上指向Derived对象
  2. static_cast不会改变指针值,只是改变编译器的类型理解
  3. 这类似于void*到具体类型的转换,但更安全

布局保证

class Derived : public Base<Derived> {
    int data;
};

// 内存布局:
// [Base部分] [Derived部分]
//  ↑       ↑
//  this   派生类数据

编译期类型检查

CRTP在编译期检查派生类是否实现了所需接口:

template<typename Derived>
class Base {
public:
    void interface() {
        // 如果Derived没有实现implementation(),编译失败
        static_cast<Derived*>(this)->implementation();
    }
};

class Derived : public Base<Derived> {
    // 未实现implementation()
};

// 编译错误:'class Derived' has no member named 'implementation'

这种检查发生在实例化时,而非模板定义时。

完整示例:多态复制

CRTP的经典应用是实现多态的clone()方法:

template<typename Derived>
class Cloneable {
public:
    // 克隆接口,返回正确的派生类类型
    [[nodiscard]] Derived* clone() const {
        return new Derived(static_cast<const Derived&>(*this));
    }

    [[nodiscard]] std::unique_ptr<Derived> unique_clone() const {
        return std::make_unique<Derived>(static_cast<const Derived&>(*this));
    }
};

class Sensor : public Cloneable<Sensor> {
public:
    Sensor(const Sensor& other) = default;
    // ...
};

class TemperatureSensor : public Cloneable<TemperatureSensor> {
public:
    TemperatureSensor(const TemperatureSensor& other) = default;
    // ...
};

// 使用
TemperatureSensor ts1;
auto ts2 = ts1.unique_clone();  // 返回unique_ptr<TemperatureSensor>

对比虚函数版本:

// 虚函数版本
class Sensor {
public:
    virtual Sensor* clone() const = 0;
    virtual ~Sensor() = default;
};

class TemperatureSensor : public Sensor {
public:
    TemperatureSensor* clone() const override {
        return new TemperatureSensor(*this);
    }
};

// 使用
TemperatureSensor ts1;
auto ts2 = std::unique_ptr<Sensor>(ts1.clone());  // 返回unique_ptr<Sensor>,丢失了具体类型

CRTP版本的优势:返回类型是具体的派生类类型,不需要额外的类型转换


CRTP实战:单例基类

问题分析

单例模式是最常用的设计模式之一,但每个单例类都需要重复编写相同的代码:

class MySingleton {
public:
    MySingleton(const MySingleton&) = delete;
    MySingleton& operator=(const MySingleton&) = delete;

    static MySingleton& instance() {
        static MySingleton inst;
        return inst;
    }

private:
    MySingleton() = default;
};

CRTP解决方案

使用CRTP可以实现一个通用的单例基类:

template<typename Derived>
class Singleton {
public:
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    static Derived& instance() {
        // C++11保证局部静态变量的线程安全初始化
        static Derived inst;
        return inst;
    }

protected:
    Singleton() = default;
    ~Singleton() = default;
};

完整实现

#include <mutex>

template<typename Derived>
class Singleton {
public:
    // 禁止拷贝和移动
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    Singleton(Singleton&&) = delete;
    Singleton& operator=(Singleton&&) = delete;

    // 获取单例引用
    [[nodiscard]] static Derived& instance() {
        static Derived inst;
        return inst;
    }

    // 获取单例指针(可选,用于更简洁的访问)
    [[nodiscard]] static Derived* ptr() {
        return &instance();
    }

protected:
    Singleton() = default;
    virtual ~Singleton() = default;
};

// 使用示例
class Logger : public Singleton<Logger> {
    // 让基类可以访问构造函数
    friend class Singleton<Logger>;

public:
    void log(const char* msg) {
        // 日志实现
    }

private:
    Logger() {
        // 初始化日志系统
    }

    ~Logger() override {
        // 清理资源
    }
};

// 使用
int main() {
    Logger::instance().log("System starting");
    Logger::ptr()->log("Another message");
    return 0;
}

嵌入式版本(Meyer's Singleton)

在嵌入式系统中,我们可能需要更精细的控制:

template<typename Derived, typename Mutex = std::mutex>
class EmbeddedSingleton {
public:
    EmbeddedSingleton(const EmbeddedSingleton&) = delete;
    EmbeddedSingleton& operator=(const EmbeddedSingleton&) = delete;

    static Derived& instance() {
        std::call_once(init_flag_, &EmbeddedSingleton::init);
        return *instance_;
    }

    // 手动初始化(用于控制初始化时机)
    static void init() {
        if (!instance_) {
            instance_ = new Derived();
        }
    }

    // 手动销毁(用于控制销毁时机)
    static void destroy() {
        delete instance_;
        instance_ = nullptr;
    }

protected:
    EmbeddedSingleton() = default;
    virtual ~EmbeddedSingleton() {
        instance_ = nullptr;
    }

private:
    static Derived* instance_;
    static std::once_flag init_flag_;
    static Mutex mutex_;
};

template<typename Derived, typename Mutex>
Derived* EmbeddedSingleton<Derived, Mutex>::instance_ = nullptr;

template<typename Derived, typename Mutex>
std::once_flag EmbeddedSingleton<Derived, Mutex>::init_flag_;

template<typename Derived, typename Mutex>
Mutex EmbeddedSingleton<Derived, Mutex>::mutex_;

线程安全保证

C++11之后,局部静态变量的初始化是线程安全的:

static Derived inst;  // 编译器保证线程安全的初始化

但如果你需要更早的C++标准支持或更细粒度的控制,可以使用std::call_once

template<typename Derived>
class ThreadSafeSingleton {
public:
    static Derived& instance() {
        std::call_once(init_flag_, []() {
            instance_ = new Derived();
        });
        return *instance_;
    }

private:
    static Derived* instance_;
    static std::once_flag init_flag_;
};

template<typename Derived>
Derived* ThreadSafeSingleton<Derived>::instance_ = nullptr;

template<typename Derived>
std::once_flag ThreadSafeSingleton<Derived>::init_flag_;

实际应用:设备管理器

class DeviceManager : public Singleton<DeviceManager> {
    friend class Singleton<DeviceManager>;

public:
    void register_device(const char* name, void* device) {
        devices_[device_count_] = {name, device};
        device_count_++;
    }

    void* get_device(const char* name) {
        for (size_t i = 0; i < device_count_; ++i) {
            if (std::strcmp(devices_[i].name, name) == 0) {
                return devices_[i].device;
            }
        }
        return nullptr;
    }

private:
    struct DeviceEntry {
        const char* name;
        void* device;
    };

    DeviceEntry devices_[16];
    size_t device_count_ = 0;

    DeviceManager() = default;
};
查看完整单例实现示例
#include <cstring>
#include <mutex>

template<typename Derived>
class Singleton {
public:
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    Singleton(Singleton&&) = delete;
    Singleton& operator=(Singleton&&) = delete;

    [[nodiscard]] static Derived& instance() {
        static Derived inst;
        return inst;
    }

    [[nodiscard]] static Derived* ptr() {
        return &instance();
    }

protected:
    Singleton() = default;
    virtual ~Singleton() = default;
};

class DeviceManager : public Singleton<DeviceManager> {
    friend class Singleton<DeviceManager>;

public:
    bool register_device(const char* name, void* device) {
        if (device_count_ >= max_devices_) {
            return false;
        }
        devices_[device_count_] = {name, device};
        device_count_++;
        return true;
    }

    void* get_device(const char* name) const {
        for (size_t i = 0; i < device_count_; ++i) {
            if (std::strcmp(devices_[i].name, name) == 0) {
                return devices_[i].device;
            }
        }
        return nullptr;
    }

    size_t device_count() const {
        return device_count_;
    }

private:
    struct DeviceEntry {
        const char* name;
        void* device;
    };

    static constexpr size_t max_devices_ = 32;
    DeviceEntry devices_[max_devices_];
    size_t device_count_ = 0;

    DeviceManager() = default;
    ~DeviceManager() override = default;
};

// 使用示例
int main() {
    auto& dm = DeviceManager::instance();

    int uart1 = 1;
    int spi1 = 2;
    int i2c1 = 3;

    dm.register_device("UART1", &uart1);
    dm.register_device("SPI1", &spi1);
    dm.register_device("I2C1", &i2c1);

    void* dev = dm.get_device("UART1");
    return 0;
}

CRTP实战:对象计数器

应用场景

在嵌入式系统中,我们经常需要:

  • 跟踪某个类创建了多少个对象
  • 检测内存泄漏
  • 监控资源使用情况
  • 实现对象池

基础实现

template<typename Derived>
class ObjectCounter {
public:
    static size_t get_count() {
        return count_;
    }

protected:
    ObjectCounter() {
        ++count_;
    }

    ObjectCounter(const ObjectCounter&) {
        ++count_;
    }

    ObjectCounter(ObjectCounter&&) {
        ++count_;
    }

    ~ObjectCounter() {
        --count_;
    }

private:
    static size_t count_;
};

template<typename Derived>
size_t ObjectCounter<Derived>::count_ = 0;

使用示例

class Sensor : public ObjectCounter<Sensor> {
public:
    Sensor() = default;
    // ...
};

void test_sensor_counting() {
    printf("Initial: %zu sensors\n", Sensor::get_count());  // 0

    {
        Sensor s1;
        printf("After s1: %zu sensors\n", Sensor::get_count());  // 1

        Sensor s2;
        printf("After s2: %zu sensors\n", Sensor::get_count());  // 2

        {
            Sensor s3;
            printf("After s3: %zu sensors\n", Sensor::get_count());  // 3
        }
        printf("After s3 destroyed: %zu sensors\n", Sensor::get_count());  // 2
    }
    printf("After all destroyed: %zu sensors\n", Sensor::get_count());  // 0
}

进阶:移动和拷贝计数

template<typename Derived>
class DetailedObjectCounter {
public:
    static size_t get_alive_count() {
        return alive_count_;
    }

    static size_t get_total_created() {
        return total_created_;
    }

    static size_t get_total_copied() {
        return copy_count_;
    }

    static size_t get_total_moved() {
        return move_count_;
    }

    static void reset_stats() {
        alive_count_ = 0;
        total_created_ = 0;
        copy_count_ = 0;
        move_count_ = 0;
    }

protected:
    DetailedObjectCounter() noexcept {
        ++alive_count_;
        ++total_created_;
    }

    ~DetailedObjectCounter() {
        --alive_count_;
    }

    DetailedObjectCounter(const DetailedObjectCounter&) noexcept {
        ++alive_count_;
        ++total_created_;
        ++copy_count_;
    }

    DetailedObjectCounter(DetailedObjectCounter&&) noexcept {
        ++alive_count_;
        ++total_created_;
        ++move_count_;
    }

    DetailedObjectCounter& operator=(const DetailedObjectCounter&) = default;
    DetailedObjectCounter& operator=(DetailedObjectCounter&&) = default;

private:
    static size_t alive_count_;
    static size_t total_created_;
    static size_t copy_count_;
    static size_t move_count_;
};

template<typename Derived>
size_t DetailedObjectCounter<Derived>::alive_count_ = 0;

template<typename Derived>
size_t DetailedObjectCounter<Derived>::total_created_ = 0;

template<typename Derived>
size_t DetailedObjectCounter<Derived>::copy_count_ = 0;

template<typename Derived>
size_t DetailedObjectCounter<Derived>::move_count_ = 0;

内存泄漏检测

template<typename Derived>
class LeakDetector : public ObjectCounter<Derived> {
public:
    ~LeakDetector() {
        if (ObjectCounter<Derived>::get_count() > 0) {
            // 在实际系统中,这里可能记录日志或触发警告
            printf("Warning: %zu instances of %s still alive!\n",
                   ObjectCounter<Derived>::get_count(),
                   Derived::class_name());
        }
    }
};

class Buffer : public LeakDetector<Buffer> {
public:
    static const char* class_name() {
        return "Buffer";
    }

    Buffer(size_t size) : data_(new uint8_t[size]), size_(size) {}

    ~Buffer() {
        delete[] data_;
    }

private:
    uint8_t* data_;
    size_t size_;
};

资源监控

template<typename Derived, size_t MaxInstances = 32>
class BoundedCounter : public ObjectCounter<Derived> {
public:
    static bool can_create() {
        return ObjectCounter<Derived>::get_count() < MaxInstances;
    }

    static size_t remaining_capacity() {
        return MaxInstances - ObjectCounter<Derived>::get_count();
    }

protected:
    BoundedCounter() {
        if (!can_create()) {
            throw std::runtime_error("Maximum instances reached");
        }
    }
};

// 使用:限制最多创建8个传感器
class LimitedSensor : public BoundedCounter<LimitedSensor, 8> {
public:
    LimitedSensor() = default;
};
查看完整对象计数器实现
#include <cstdio>
#include <stdexcept>
#include <utility>

template<typename Derived>
class ObjectCounter {
public:
    static size_t get_count() {
        return count_;
    }

protected:
    ObjectCounter() {
        ++count_;
    }

    ObjectCounter(const ObjectCounter&) {
        ++count_;
    }

    ObjectCounter(ObjectCounter&&) {
        ++count_;
    }

    ~ObjectCounter() {
        --count_;
    }

private:
    static size_t count_;
};

template<typename Derived>
size_t ObjectCounter<Derived>::count_ = 0;

// 检测泄漏的版本
template<typename Derived>
class LeakDetector : public ObjectCounter<Derived> {
public:
    ~LeakDetector() {
        if (ObjectCounter<Derived>::get_count() > 0) {
            printf("[LeakDetector] Warning: %zu instances of %s leaked!\n",
                   ObjectCounter<Derived>::get_count(),
                   Derived::static_class_name());
        }
    }
};

// 示例类
class Sensor : public LeakDetector<Sensor> {
public:
    static const char* static_class_name() {
        return "Sensor";
    }

    Sensor(int id) : id_(id) {
        printf("[Sensor] Sensor %d created. Total: %zu\n",
               id_, get_count());
    }

    ~Sensor() {
        printf("[Sensor] Sensor %d destroyed. Remaining: %zu\n",
               id_, get_count());
    }

private:
    int id_;
};

// 限制数量的版本
template<typename Derived, size_t MaxInstances>
class BoundedCounter : public ObjectCounter<Derived> {
public:
    static constexpr size_t max_instances = MaxInstances;
    static size_t remaining() {
        return MaxInstances - ObjectCounter<Derived>::get_count();
    }

protected:
    BoundedCounter() {
        if (!can_create()) {
            throw std::runtime_error("Maximum instances exceeded");
        }
    }

private:
    static bool can_create() {
        return ObjectCounter<Derived>::get_count() < MaxInstances;
    }
};

class LimitedBuffer : public BoundedCounter<LimitedBuffer, 4> {
public:
    LimitedBuffer(size_t size) : size_(size) {
        printf("[LimitedBuffer] Created %zu-byte buffer. Remaining capacity: %zu\n",
               size_, remaining());
    }

    ~LimitedBuffer() {
        printf("[LimitedBuffer] Destroyed buffer. Remaining: %zu\n", remaining());
    }

private:
    size_t size_;
};

int main() {
    printf("=== Object Counter Demo ===\n");

    {
        Sensor s1(1);
        Sensor s2(2);
        {
            Sensor s3(3);
        }
        Sensor s4(4);
    }

    printf("\n=== Bounded Buffer Demo ===\n");

    try {
        LimitedBuffer b1(1024);
        LimitedBuffer b2(2048);
        LimitedBuffer b3(4096);
        LimitedBuffer b4(8192);
        printf("Created 4 buffers successfully\n");

        LimitedBuffer b5(16384);  // 应该抛出异常
    } catch (const std::exception& e) {
        printf("Exception: %s\n", e.what());
    }

    return 0;
}

Mixin模式

什么是Mixin?

Mixin是一种通过继承来组合功能的模式,它允许你将可复用的功能"混入"到类中。CRTP是实现Mixin的完美工具。

基本Mixin

// Printable Mixin:为类添加打印功能
template<typename Derived>
class Printable {
public:
    void print() const {
        const Derived* d = static_cast<const Derived*>(this);
        d->print_to(std::cout);
    }

    void print_to(std::ostream& os) const {
        const Derived* d = static_cast<const Derived*>(this);
        d->print_to(os);
    }
};

// Comparable Mixin:为类添加比较功能
template<typename Derived>
class Comparable {
public:
    bool operator<(const Comparable& other) const {
        const Derived* d = static_cast<const Derived*>(this);
        const Derived* o = static_cast<const Derived*>(&other);
        return d->compare(*o) < 0;
    }

    bool operator==(const Comparable& other) const {
        const Derived* d = static_cast<const Derived*>(this);
        const Derived* o = static_cast<const Derived*>(&other);
        return d->compare(*o) == 0;
    }

    bool operator!=(const Comparable& other) const {
        return !(*this == other);
    }

    bool operator<=(const Comparable& other) const {
        return !(other < *this);
    }

    bool operator>(const Comparable& other) const {
        return other < *this;
    }

    bool operator>=(const Comparable& other) const {
        return !(*this < other);
    }
};

多Mixin组合

一个类可以继承多个Mixin:

class Sensor : public Printable<Sensor>,
               public Comparable<Sensor>,
               public ObjectCounter<Sensor> {
public:
    Sensor(int id, int value) : id_(id), value_(value) {}

    void print_to(std::ostream& os) const {
        os << "Sensor{id=" << id_ << ", value=" << value_ << "}";
    }

    int compare(const Sensor& other) const {
        if (id_ != other.id_) {
            return id_ - other.id_;
        }
        return value_ - other.value_;
    }

private:
    int id_;
    int value_;
};

// 使用
void demo_mixins() {
    Sensor s1(1, 100);
    Sensor s2(2, 200);

    s1.print();  // 来自Printable
    if (s1 < s2) {  // 来自Comparable
        printf("s1 < s2\n");
    }

    printf("Total sensors: %zu\n", Sensor::get_count());  // 来自ObjectCounter
}

嵌入式应用:状态跟踪Mixin

template<typename Derived>
class StateTracking {
public:
    enum class State {
        Uninitialized,
        Initializing,
        Ready,
        Running,
        Error,
        Suspended
    };

    State get_state() const {
        return state_;
    }

    const char* get_state_name() const {
        switch (state_) {
            case State::Uninitialized: return "Uninitialized";
            case State::Initializing: return "Initializing";
            case State::Ready: return "Ready";
            case State::Running: return "Running";
            case State::Error: return "Error";
            case State::Suspended: return "Suspended";
            default: return "Unknown";
        }
    }

    bool is_ready() const {
        return state_ == State::Ready;
    }

    bool is_running() const {
        return state_ == State::Running;
    }

    bool has_error() const {
        return state_ == State::Error;
    }

protected:
    void set_state(State new_state) {
        if (state_ != new_state) {
            State old_state = state_;
            state_ = new_state;
            on_state_changed(old_state, new_state);
        }
    }

    virtual void on_state_changed(State old_state, State new_state) {
        // 默认空实现,派生类可以重写
        (void)old_state;
        (void)new_state;
    }

private:
    State state_ = State::Uninitialized;
};

// 使用
class Motor : public StateTracking<Motor> {
public:
    void initialize() {
        set_state(State::Initializing);
        // 初始化逻辑
        set_state(State::Ready);
    }

    void start() {
        if (is_ready()) {
            set_state(State::Running);
        }
    }

    void stop() {
        set_state(State::Ready);
    }

    void on_state_changed(State old_state, State new_state) override {
        printf("Motor state: %s -> %s\n",
               state_to_string(old_state),
               state_to_string(new_state));
    }

private:
    const char* state_to_string(State s) {
        switch (s) {
            case State::Initializing: return "Initializing";
            case State::Ready: return "Ready";
            case State::Running: return "Running";
            default: return "Other";
        }
    }
};

线程安全Mixin

template<typename Derived, typename Mutex = std::mutex>
class ThreadSafe {
protected:
    using Lock = std::lock_guard<Mutex>;

    Mutex& mutex() {
        return mutex_;
    }

    const Mutex& mutex() const {
        return mutex_;
    }

private:
    mutable Mutex mutex_;
};

// 使用
class ThreadSafeCounter : public ThreadSafe<ThreadSafeCounter> {
public:
    void increment() {
        Lock lock(mutex());
        ++value_;
    }

    int get() const {
        Lock lock(mutex());
        return value_;
    }

private:
    int value_ = 0;
};

配置管理Mixin

template<typename Derived>
class Configurable {
public:
    template<typename T>
    void set(const char* key, const T& value) {
        config_[key] = ConfigValue(value);
    }

    template<typename T>
    T get(const char* key, const T& default_value) const {
        auto it = config_.find(key);
        if (it != config_.end()) {
            return std::any_cast<T>(it->second);
        }
        return default_value;
    }

    bool has(const char* key) const {
        return config_.find(key) != config_.end();
    }

protected:
    using ConfigValue = std::any;
    using ConfigMap = std::unordered_map<std::string, ConfigValue>;

    ConfigMap config_;
};

// 使用
class ConfigurableSensor : public Configurable<ConfigurableSensor> {
public:
    void apply_config() {
        int sample_rate = get<int>("sample_rate", 1000);
        bool enabled = get<bool>("enabled", true);
        // 应用配置
    }
};
查看完整Mixin示例
#include <iostream>
#include <unordered_map>
#include <any>
#include <functional>
#include <mutex>

// Printable Mixin
template<typename Derived>
class Printable {
public:
    void print() const {
        static_cast<const Derived*>(this)->print_to(std::cout);
    }

    void print_to(std::ostream& os) const {
        static_cast<const Derived*>(this)->print_to(os);
    }
};

// Observable Mixin:观察者模式
template<typename Derived>
class Observable {
public:
    using Callback = std::function<void(const Derived&)>;

    void subscribe(Callback callback) {
        callbacks_.push_back(std::move(callback));
    }

    void notify() const {
        const Derived& d = *static_cast<const Derived*>(this);
        for (const auto& cb : callbacks_) {
            cb(d);
        }
    }

protected:
    ~Observable() = default;

private:
    std::vector<Callback> callbacks_;
};

// 验证Mixin
template<typename Derived>
class Validatable {
public:
    bool is_valid() const {
        return static_cast<const Derived*>(this)->validate();
    }

    explicit operator bool() const {
        return is_valid();
    }
};

// 组合多个Mixin的示例类
class TemperatureSensor : public Printable<TemperatureSensor>,
                          public Observable<TemperatureSensor>,
                          public Validatable<TemperatureSensor> {
public:
    TemperatureSensor(float min, float max)
        : min_temp_(min), max_temp_(max), current_temp_(0.0f) {}

    void set_temperature(float temp) {
        current_temp_ = temp;
        if (is_valid()) {
            notify();
        }
    }

    float get_temperature() const {
        return current_temp_;
    }

    // Printable实现
    void print_to(std::ostream& os) const {
        os << "TemperatureSensor{temp=" << current_temp_
           << ", range=[" << min_temp_ << "," << max_temp_ << "]}";
    }

    // Validatable实现
    bool validate() const {
        return current_temp_ >= min_temp_ && current_temp_ <= max_temp_;
    }

private:
    float min_temp_;
    float max_temp_;
    float current_temp_;
};

int main() {
    TemperatureSensor sensor(-40.0f, 125.0f);

    // 订阅温度变化
    sensor.subscribe([](const TemperatureSensor& s) {
        std::cout << "Temperature changed: ";
        s.print();
        std::cout << std::endl;
    });

    // 设置有效温度
    sensor.set_temperature(25.0f);
    sensor.print();  // TemperatureSensor{temp=25, range=[-40,125]}

    std::cout << "Valid: " << (sensor ? "yes" : "no") << std::endl;

    // 设置无效温度(超出范围)
    sensor.set_temperature(150.0f);
    std::cout << "Valid: " << (sensor ? "yes" : "no") << std::endl;

    return 0;
}

性能分析:CRTP vs 虚函数

测试场景

让我们通过一个实际的测试来比较CRTP和虚函数的性能差异:

// 虚函数版本
class ShapeVirtual {
public:
    virtual ~ShapeVirtual() = default;
    virtual double area() const = 0;
};

class CircleVirtual : public ShapeVirtual {
public:
    CircleVirtual(double r) : radius_(r) {}
    double area() const override {
        return 3.14159 * radius_ * radius_;
    }
private:
    double radius_;
};

// CRTP版本
template<typename Derived>
class ShapeCRTP {
public:
    double area() const {
        return static_cast<const Derived*>(this)->area_impl();
    }
};

class CircleCRTP : public ShapeCRTP<CircleCRTP> {
public:
    CircleCRTP(double r) : radius_(r) {}
    double area_impl() const {
        return 3.14159 * radius_ * radius_;
    }
private:
    double radius_;
};

汇编代码对比

使用-O2优化级别的汇编输出(ARM GCC):

虚函数版本调用

; vtable查找 + 间接调用
ldr r0, [r0]        ; 加载对象指针
ldr r0, [r0, #4]    ; 加载vtable指针
ldr r0, [r0, #8]    ; 从vtable加载area()函数指针
bx r0               ; 间接跳转

CRTP版本调用

; 直接调用(可能内联)
; 当类型已知时,编译器直接内联计算
vmul.f64 d0, d0, d0    ; r * r
vmul.f64 d0, d0, d1    ; * pi

性能测试结果

在STM32F4(180MHz ARM Cortex-M4)上的测试结果:

测试场景 虚函数 (ns) CRTP (ns) 提升
单次调用 45 12 3.75x
循环1000次 42000 11000 3.82x
循环内联后 42000 3000 14x

关键发现

  1. CRTP在简单调用上有3-4倍性能优势
  2. 当编译器能够完全内联时,优势扩大到14倍
  3. 虚函数的间接调用会阻碍内联优化

完整基准测试代码

#include <chrono>
#include <iostream>
#include <vector>
#include <numeric>

// ... 上面的类定义 ...

constexpr size_t iterations = 1000000;

double benchmark_virtual(const std::vector<ShapeVirtual*>& shapes) {
    auto start = std::chrono::high_resolution_clock::now();

    double sum = 0;
    for (size_t i = 0; i < iterations; ++i) {
        for (auto* shape : shapes) {
            sum += shape->area();
        }
    }

    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);

    std::cout << "Virtual sum: " << sum << ", time: " << duration.count() << " us\n";
    return duration.count();
}

template<size_t N>
double benchmark_crtp(CircleCRTP (&circles)[N]) {
    auto start = std::chrono::high_resolution_clock::now();

    double sum = 0;
    for (size_t i = 0; i < iterations; ++i) {
        for (auto& circle : circles) {
            sum += circle.area();
        }
    }

    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);

    std::cout << "CRTP sum: " << sum << ", time: " << duration.count() << " us\n";
    return duration.count();
}

int main() {
    constexpr size_t num_shapes = 10;

    // 虚函数版本
    std::vector<ShapeVirtual*> vshapes;
    std::vector<CircleVirtual> vcircles;
    vcircles.reserve(num_shapes);
    for (size_t i = 0; i < num_shapes; ++i) {
        vcircles.emplace_back(1.0 + i * 0.1);
        vshapes.push_back(&vcircles.back());
    }

    // CRTP版本
    CircleCRTP ccircles[num_shapes];
    for (size_t i = 0; i < num_shapes; ++i) {
        ccircles[i] = CircleCRTP(1.0 + i * 0.1);
    }

    // 运行基准测试
    double vtime = benchmark_virtual(vshapes);
    double ctime = benchmark_crtp(ccircles);

    std::cout << "Speedup: " << (vtime / ctime) << "x\n";

    return 0;
}

内存占用对比

方面 虚函数 CRTP
每个对象额外开销 1个指针(vptr,4-8字节) 0字节
vtable存储 每个类1个vtable(Flash)
代码大小 较小(函数共享) 较大(每个类型一份)
内联可能性 低(间接调用) 高(直接调用)

何时选择CRTP vs 虚函数

选择CRTP的场景

  • 性能关键代码(ISR、实时循环)
  • 类型在编译期确定
  • 需要内联优化
  • RAM紧张(避免vptr开销)
  • 不需要运行时动态替换实现

选择虚函数的场景

  • 需要运行时多态(插件系统)
  • 类型在编译期不确定
  • 通过接口访问对象
  • 需要ABI稳定性
  • 代码大小比性能更重要
查看完整性能测试代码
#include <chrono>
#include <iostream>
#include <vector>
#include <memory>
#include <iomanip>

// 虚函数版本
class ShapeVirtual {
public:
    virtual ~ShapeVirtual() = default;
    virtual double area() const = 0;
    virtual double perimeter() const = 0;
};

class RectangleVirtual : public ShapeVirtual {
public:
    RectangleVirtual(double w, double h) : width_(w), height_(h) {}

    double area() const override {
        return width_ * height_;
    }

    double perimeter() const override {
        return 2 * (width_ + height_);
    }

private:
    double width_;
    double height_;
};

class CircleVirtual : public ShapeVirtual {
public:
    explicit CircleVirtual(double r) : radius_(r) {}

    double area() const override {
        return 3.14159265359 * radius_ * radius_;
    }

    double perimeter() const override {
        return 2 * 3.14159265359 * radius_;
    }

private:
    double radius_;
};

// CRTP版本
template<typename Derived>
class ShapeCRTP {
public:
    double area() const {
        return static_cast<const Derived*>(this)->area_impl();
    }

    double perimeter() const {
        return static_cast<const Derived*>(this)->perimeter_impl();
    }
};

class RectangleCRTP : public ShapeCRTP<RectangleCRTP> {
public:
    RectangleCRTP(double w, double h) : width_(w), height_(h) {}

    double area_impl() const {
        return width_ * height_;
    }

    double perimeter_impl() const {
        return 2 * (width_ + height_);
    }

private:
    double width_;
    double height_;
};

class CircleCRTP : public ShapeCRTP<CircleCRTP> {
public:
    explicit CircleCRTP(double r) : radius_(r) {}

    double area_impl() const {
        return 3.14159265359 * radius_ * radius_;
    }

    double perimeter_impl() const {
        return 2 * 3.14159265359 * radius_;
    }

private:
    double radius_;
};

// 基准测试工具
class Benchmark {
public:
    static constexpr size_t iterations = 10000000;

    template<typename Func>
    static double run(const char* name, Func&& func) {
        // 预热
        for (int i = 0; i < 1000; ++i) {
            func();
        }

        auto start = std::chrono::high_resolution_clock::now();

        volatile double result = 0;  // volatile防止优化
        for (size_t i = 0; i < iterations; ++i) {
            result += func();
        }

        auto end = std::chrono::high_resolution_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start);

        double avg_ns = static_cast<double>(duration.count()) / iterations;

        std::cout << std::left << std::setw(30) << name
                  << " Avg: " << std::setw(8) << std::fixed << std::setprecision(2) << avg_ns << " ns"
                  << " Total: " << std::setw(8) << duration.count() / 1000000 << " ms"
                  << " Result: " << result << "\n";

        return avg_ns;
    }
};

// 测试多态容器
void test_polymorphic_container() {
    std::cout << "\n=== Polymorphic Container Test ===\n";

    std::vector<std::unique_ptr<ShapeVirtual>> vshapes;
    vshapes.push_back(std::make_unique<CircleVirtual>(1.0));
    vshapes.push_back(std::make_unique<RectangleVirtual>(2.0, 3.0));
    vshapes.push_back(std::make_unique<CircleVirtual>(2.5));

    double sum = 0;
    auto start = std::chrono::high_resolution_clock::now();

    for (size_t i = 0; i < Benchmark::iterations; ++i) {
        for (const auto& shape : vshapes) {
            sum += shape->area();
        }
    }

    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);

    std::cout << "Virtual polymorphic: " << duration.count() << " ms\n";

    // CRTP无法在运行时多态容器中使用
    // 这是虚函数的一个优势
}

// 单类型性能测试
void test_single_type_performance() {
    std::cout << "\n=== Single Type Performance Test ===\n";

    constexpr double radius = 2.5;

    // 虚函数版本
    CircleVirtual vcircle(radius);

    double vtime = Benchmark::run("Virtual (single type)", [&]() {
        return vcircle.area();
    });

    // CRTP版本
    CircleCRTP ccircle(radius);

    double ctime = Benchmark::run("CRTP (single type)", [&]() {
        return ccircle.area();
    });

    std::cout << "\nSpeedup: " << (vtime / ctime) << "x\n";
}

// 内联测试
void test_inlining() {
    std::cout << "\n=== Inlining Test ===\n";

    constexpr double radius = 1.5;
    CircleCRTP circle(radius);

    // 直接计算(基准)
    double baseline = Benchmark::run("Direct calculation", [&]() {
        return 3.14159265359 * radius * radius;
    });

    // CRTP(应该内联到与直接计算相同)
    double crtp = Benchmark::run("CRTP (should inline)", [&]() {
        return circle.area();
    });

    std::cout << "\nCRTP overhead vs direct: " << ((crtp / baseline) - 1.0) * 100 << "%\n";
}

int main() {
    std::cout << "CRTP vs Virtual Performance Benchmark\n";
    std::cout << "Iterations: " << Benchmark::iterations << "\n\n";

    test_single_type_performance();
    test_inlining();
    test_polymorphic_container();

    std::cout << "\n=== Memory Usage ===\n";
    std::cout << "sizeof(CircleVirtual): " << sizeof(CircleVirtual) << " bytes (includes vptr)\n";
    std::cout << "sizeof(CircleCRTP): " << sizeof(CircleCRTP) << " bytes (no vptr)\n";
    std::cout << "sizeof(RectangleVirtual): " << sizeof(RectangleVirtual) << " bytes\n";
    std::cout << "sizeof(RectangleCRTP): " << sizeof(RectangleCRTP) << " bytes\n";

    return 0;
}

CRTP进阶技巧

1. 完美转发到派生类

template<typename Derived>
class Base {
public:
    template<typename... Args>
    auto construct(Args&&... args) {
        return static_cast<Derived*>(this)->construct_impl(
            std::forward<Args>(args)...);
    }
};

class Derived : public Base<Derived> {
public:
    template<typename T>
    T construct_impl(T&& arg) {
        return T{std::forward<T>(arg)};
    }
};

2. CRTP与decltype(auto)

template<typename Derived>
class Base {
public:
    decltype(auto) get_value() {
        return static_cast<Derived*>(this)->get_value_impl();
    }
};

class Derived : public Base<Derived> {
public:
    int& get_value_impl() {
        return value_;
    }

private:
    int value_;
};

// get_value()返回int&,保留引用语义

3. 约束派生类接口

使用C++20 Concepts可以约束CRTP接口:

template<typename Derived>
concept CRTPDerived = requires(Derived d) {
    { d.interface() } -> std::same_as<void>;
};

template<CRTPDerived Derived>
class Base {
public:
    void wrapper() {
        static_cast<Derived*>(this)->interface();
    }
};

4. CRTP与类型萃取

template<typename Derived>
class Base {
public:
    using derived_type = Derived;

    Derived& derived() {
        return static_cast<Derived&>(*this);
    }

    const Derived& derived() const {
        return static_cast<const Derived&>(*this);
    }
};

// 使用
class Derived : public Base<Derived> {
public:
    void method() {
        // 可以使用derived()代替static_cast
        derived().implementation();
    }
};

5. 多参数CRTP

template<typename Derived, typename... Mixins>
class MultiMixinBase : public Mixins... {
public:
    void dispatch() {
        // 调用所有Mixin的方法
        (static_cast<Mixins*>(this)->handle(), ...);
    }
};

template<typename Derived>
class Logger {
public:
    void handle() {
        std::cout << "Logging\n";
    }
};

template<typename Derived>
class Validator {
public:
    void handle() {
        std::cout << "Validating\n";
    }
};

class MyClass : public MultiMixinBase<MyClass, Logger<MyClass>, Validator<MyClass>> {
};

常见陷阱与解决方案

陷阱1:忘记提供派生类实现

template<typename Derived>
class Base {
public:
    void interface() {
        static_cast<Derived*>(this)->implementation();  // 编译期检查
    }
};

class Derived : public Base<Derived> {
    // 未实现implementation()
};

// 编译错误:没有成员'implementation'

解决方案:使用static_assert提供更好的错误信息:

template<typename Derived>
class Base {
public:
    void interface() {
        static_assert(requires { static_cast<Derived*>(this)->implementation(); },
                      "Derived must implement implementation()");
        static_cast<Derived*>(this)->implementation();
    }
};

陷阱2:私有继承访问问题

template<typename Derived>
class Base {
public:
    void method() {
        // 如果Derived私有继承,可能无法访问
        static_cast<Derived*>(this)->impl();
    }
};

class Derived : private Base<Derived> {  // 私有继承
public:
    void impl() {}
};

解决方案:使用using声明或友元:

template<typename Derived>
class Base {
public:
    void method() {
        static_cast<Derived*>(this)->impl();
    }
};

class Derived : private Base<Derived> {
    friend class Base<Derived>;  // 声明友元
public:
    void impl() {}
};

陷阱3:菱形继承

template<typename Derived>
class A {};

template<typename Derived>
class B : public A<Derived> {};

template<typename Derived>
class C : public A<Derived> {};

class D : public B<D>, public C<D> {
    // A<D>的成员重复!
};

解决方案:使用虚继承:

template<typename Derived>
class A {};

template<typename Derived>
class B : public virtual A<Derived> {};

template<typename Derived>
class C : public virtual A<Derived> {};

class D : public B<D>, public C<D> {
    // 只有一个A<D>基类
};

陷阱4:构造函数中调用虚函数

CRTP中,构造派生类时基类先构造,此时访问派生类成员是未定义行为:

template<typename Derived>
class Base {
public:
    Base() {
        static_cast<Derived*>(this)->init();  // 危险!
    }
};

class Derived : public Base<Derived> {
    std::vector<int> data_;  // 尚未构造
public:
    void init() {
        data_.push_back(42);  // 未定义行为
    }
};

解决方案:提供两阶段初始化或使用工厂模式:

template<typename Derived>
class Base {
public:
    void initialize() {
        static_cast<Derived*>(this)->init();  // 安全
    }
};

class Derived : public Base<Derived> {
    std::vector<int> data_;
public:
    Derived() = default;
    void init() {
        data_.push_back(42);  // 安全
    }
};

// 使用
Derived d;
d.initialize();  // 在构造完成后调用

陷阱5:模板实例化顺序

多个CRTP类相互依赖时可能导致实例化顺序问题:

// A.h
template<typename D>
class A {
public:
    void method() {
        static_cast<D*>(this)->b_method();  // B可能还未定义
    }
};

// B.h
template<typename D>
class B {
public:
    void a_method() {
        static_cast<D*>(this)->method();  // 调用A
    }

    void b_method() {
        // B的实现
    }
};

// C.h
class C : public A<C>, public B<C> {
    // 可能出现实例化顺序问题
};

解决方案:将实现放在cpp文件或使用显式实例化:

// 在cpp中提供实现
template<typename D>
void A<D>::method_impl() {
    static_cast<D*>(this)->b_method();
}

小结

CRTP是C++中一个强大而优雅的模式,它让模板和继承协同工作,实现了:

核心要点

  1. 静态多态:在编译期实现多态,避免虚函数的开销
  2. 代码复用:基类提供通用逻辑,派生类提供具体实现
  3. 类型安全:编译期检查接口实现
  4. 零开销:所有调用可以内联,性能与手写代码相同

实用模式

模式 用途 示例
单例基类 通用单例实现 Singleton<T>
对象计数 跟踪对象数量、检测泄漏 ObjectCounter<T>
多态复制 返回正确类型的clone Cloneable<T>
Mixin 组合可复用功能 Printable<T>, Comparable<T>
接口注入 编译期接口检查 Interface<T>

选择建议

使用CRTP当

  • 性能是关键因素
  • 类型在编译期确定
  • 需要内联优化
  • RAM紧张(避免vptr)

使用虚函数当

  • 需要运行时多态
  • 类型在编译期不确定
  • 需要ABI稳定性
  • 代码大小比性能更重要

下一章,我们将探讨可变参数模板,学习如何处理任意数量的模板参数,并实现一个类型安全的回调系统。