crtp(curiously recurring template pattern,奇异递归模板模式)是c++中一种高级的模板编程技术,它通过将派生类作为基类的模板参数来实现编译期多态。
1. crtp的基本概念
基本结构
// 基类模板
template<typename derived>
class base {
public:
void interface() {
// 将调用转发给派生类的实现
static_cast<derived*>(this)->implementation();
}
void static_interface() {
// 调用派生类的静态方法
derived::static_implementation();
}
};
// 派生类
class derived : public base<derived> { // 关键:将自己作为模板参数
public:
void implementation() {
std::cout << "derived implementation" << std::endl;
}
static void static_implementation() {
std::cout << "derived static implementation" << std::endl;
}
};2. crtp的核心原理
编译期多态
template<typename derived>
class base {
public:
// 编译期多态:调用哪个implementation在编译时确定
void foo() {
static_cast<derived*>(this)->implementation();
}
// 可以添加默认实现
void bar() {
std::cout << "base default implementation" << std::endl;
}
};3. crtp的常见应用
应用1:静态多态(编译期多态)
template<typename derived>
class shape {
public:
void draw() const {
// 编译期调用派生类的具体实现
static_cast<const derived*>(this)->draw_impl();
}
double area() const {
return static_cast<const derived*>(this)->area_impl();
}
};
class circle : public shape<circle> {
private:
double radius;
public:
circle(double r) : radius(r) {}
// 实现基类期望的方法
void draw_impl() const {
std::cout << "drawing circle with radius " << radius << std::endl;
}
double area_impl() const {
return 3.14159 * radius * radius;
}
};
class square : public shape<square> {
private:
double side;
public:
square(double s) : side(s) {}
void draw_impl() const {
std::cout << "drawing square with side " << side << std::endl;
}
double area_impl() const {
return side * side;
}
};
// 使用
template<typename t>
void processshape(const shape<t>& shape) {
shape.draw();
std::cout << "area: " << shape.area() << std::endl;
}
int main() {
circle circle(5.0);
square square(4.0);
processshape(circle); // 编译时生成circle版本
processshape(square); // 编译时生成square版本
}应用2:计数器模式
// 统计某个类创建的对象数量
template<typename t>
class counter {
protected:
counter() { ++count; }
counter(const counter&) { ++count; }
counter(counter&&) { ++count; }
~counter() { --count; }
public:
static int getcount() { return count; }
private:
static int count;
};
// 静态成员初始化
template<typename t>
int counter<t>::count = 0;
// 使用
class myclass1 : public counter<myclass1> {
// ...
};
class myclass2 : public counter<myclass2> {
// ...
};
int main() {
myclass1 a, b, c;
myclass2 x, y;
std::cout << "myclass1 count: " << myclass1::getcount() << std::endl; // 3
std::cout << "myclass2 count: " << myclass2::getcount() << std::endl; // 2
// 注意:每个counter实例都有自己的静态count
}应用3:多态拷贝(虚拟构造函数)
template<typename derived>
class cloneable {
public:
// 虚拟构造函数模式
derived* clone() const {
return new derived(static_cast<const derived&>(*this));
}
protected:
// 防止直接实例化
cloneable() = default;
~cloneable() = default;
};
class concreteclass : public cloneable<concreteclass> {
public:
int value;
concreteclass(int v) : value(v) {}
// 自动获得clone()方法
// 可以调用clone()来创建副本
};
int main() {
concreteclass obj1(42);
concreteclass* obj2 = obj1.clone();
std::cout << obj2->value << std::endl; // 42
delete obj2;
}应用4:静态接口检查
// 混入模式:为类添加功能
template<typename derived>
class comparable {
public:
bool operator==(const derived& other) const {
return !(static_cast<const derived&>(*this) < other) &&
!(other < static_cast<const derived&>(*this));
}
bool operator!=(const derived& other) const {
return !(*this == other);
}
bool operator<=(const derived& other) const {
return !(other < static_cast<const derived&>(*this));
}
bool operator>=(const derived& other) const {
return !(static_cast<const derived&>(*this) < other);
}
bool operator>(const derived& other) const {
return other < static_cast<const derived&>(*this);
}
};
// 只需要实现operator<,自动获得所有比较操作
class myint : public comparable<myint> {
public:
int value;
myint(int v) : value(v) {}
bool operator<(const myint& other) const {
return value < other.value;
}
};
int main() {
myint a(10), b(20), c(10);
std::cout << std::boolalpha;
std::cout << (a < b) << std::endl; // true
std::cout << (a == b) << std::endl; // false
std::cout << (a == c) << std::endl; // true
std::cout << (a != b) << std::endl; // true
std::cout << (a <= c) << std::endl; // true
}4. crtp的高级用法
访问派生类成员
template<typename derived>
class accessderived {
public:
void printinfo() {
derived* derived = static_cast<derived*>(this);
// 访问派生类的protected成员
std::cout << "value: " << derived->value << std::endl;
// 调用派生类的protected方法
derived->protectedmethod();
}
protected:
// 基类可以提供一些默认实现
virtual void protectedmethod() {
std::cout << "base protected method" << std::endl;
}
};
class myderived : public accessderived<myderived> {
friend class accessderived<myderived>; // 允许基类访问protected成员
protected:
int value = 42;
void protectedmethod() override {
std::cout << "derived protected method" << std::endl;
}
};
多级crtp
template<typename derived>
class level1 {
public:
void level1method() {
std::cout << "level1 calling: ";
static_cast<derived*>(this)->implement();
}
};
template<typename derived>
class level2 : public level1<derived> {
public:
void level2method() {
std::cout << "level2 calling: ";
static_cast<derived*>(this)->implement();
}
};
class finalclass : public level2<finalclass> {
public:
void implement() {
std::cout << "finalclass implementation" << std::endl;
}
};crtp + 策略模式
// 策略接口
template<typename t>
class serializationstrategy {
public:
std::string serialize(const t& obj) const {
return static_cast<const t*>(this)->serializeimpl();
}
};
// 具体策略
class jsonserialization : public serializationstrategy<jsonserialization> {
public:
std::string serializeimpl() const {
return "{ \"type\": \"json\" }";
}
};
class xmlserialization : public serializationstrategy<xmlserialization> {
public:
std::string serializeimpl() const {
return "<type>xml</type>";
}
};
// 使用策略的类
template<typename serializationstrategy>
class dataprocessor {
private:
serializationstrategy serializer;
public:
std::string process() {
return serializer.serialize(serializer);
}
};6. crtp的最佳实践
实践1:使用类型检查
template<typename derived>
class base {
// 编译时检查:确保derived是从base<derived>派生的
static_assert(std::is_base_of<base<derived>, derived>::value,
"derived must inherit from base<derived>");
// c++17的更简洁写法
static_assert(std::is_base_of_v<base, derived>);
};实践2:保护构造函数
template<typename derived>
class base {
protected:
// 防止直接实例化base
base() = default;
~base() = default;
// 防止在堆上创建base
void* operator new(std::size_t) = delete;
void operator delete(void*) = delete;
};实践3:使用crtp实现mixin
// mixin:为类添加功能
template<template<typename> class... mixins>
class mixedclass : public mixins<mixedclass<mixins...>>... {
// 继承多个mixin
};
// 定义mixin
template<typename derived>
class printable {
public:
void print() const {
std::cout << "printing..." << std::endl;
}
};
template<typename derived>
class serializable {
public:
std::string serialize() const {
return "serialized";
}
};
// 使用
using myclass = mixedclass<printable, serializable>;7. crtp在实际项目中的应用
示例:数学向量库
template<typename derived, typename t>
class vectorexpression {
public:
size_t size() const {
return static_cast<const derived*>(this)->size();
}
t operator[](size_t i) const {
return static_cast<const derived*>(this)->operator[](i);
}
// 延迟计算:表达式模板
derived& operator+=(const vectorexpression& other) {
derived& self = *static_cast<derived*>(this);
for (size_t i = 0; i < size(); ++i) {
self[i] += other[i];
}
return self;
}
};
template<typename t>
class vector : public vectorexpression<vector<t>, t> {
private:
std::vector<t> data;
public:
vector(size_t n, t val = t{}) : data(n, val) {}
size_t size() const { return data.size(); }
t operator[](size_t i) const { return data[i]; }
t& operator[](size_t i) { return data[i]; }
// 允许从任何vectorexpression构造
template<typename e>
vector(const vectorexpression<e, t>& expr) : data(expr.size()) {
for (size_t i = 0; i < expr.size(); ++i) {
data[i] = expr[i];
}
}
};8. crtp的局限性
- 编译时绑定:不能在运行时改变行为
- 代码膨胀:每个模板实例都会生成新代码
- 复杂的错误信息:模板错误信息难以理解
- 无法处理异质集合:需要知道具体类型
- 循环依赖:基类需要知道派生类的完整定义
9. 何时使用crtp
适合使用crtp的情况:
- 需要静态多态(性能关键)
- 实现混入(mixin)功能
- 编译期策略选择
- 为类族添加通用功能
- 表达式模板
不适合使用crtp的情况:
- 需要运行时多态
- 类型在运行时确定
- 需要存储异质对象的容器
- 项目对二进制大小敏感
总结
crtp是c++模板元编程中的强大工具,它通过编译期多态提供了零开销的抽象能力。虽然学习曲线较陡,但在性能敏感的场景下,crtp可以替代虚函数,提供更好的运行时性能。
记住crtp的核心思想:基类通过static_cast将this指针转换为派生类指针,从而调用派生类的方法,所有这一切都在编译期完成。
到此这篇关于c++ crtp模式的使用小结的文章就介绍到这了,更多相关c++ crtp模式内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论