CRTP(Curiously Recurring Template Pattern)


C++ 中的 CRTP(奇异递归模板模式)

CRTP(Curiously Recurring Template Pattern)是一种利用模板继承实现 静态多态(Static Polymorphism) 的设计模式。通过基类模板以派生类作为模板参数,CRTP 允许在编译期实现多态行为,避免虚函数开销,同时提供灵活的类型操作。以下通过代码和底层原理全面解析其实现和应用。


1. CRTP 的基本结构
1.1 核心思想
  • 基类模板:接受派生类作为模板参数。
  • 派生类:继承自以自身为模板参数的基类模板。
// 基类模板
template <typename Derived>
class Base {
public:// 通过静态转换访问派生类Derived& derived() { return static_cast<Derived&>(*this); }
};// 派生类继承自以自身为模板参数的基类
class MyDerived : public Base<MyDerived> {// 派生类的实现
};

2. CRTP 的典型应用场景
2.1 静态多态(替代虚函数)
template <typename Derived>
class Shape {
public:void draw() {// 调用派生类的具体实现static_cast<Derived*>(this)->drawImpl();}
};class Circle : public Shape<Circle> {
public:void drawImpl() { std::cout << "Drawing Circle" << std::endl; }
};class Square : public Shape<Square> {
public:void drawImpl() { std::cout << "Drawing Square" << std::endl; }
};int main() {Circle circle;Square square;circle.draw();  // 输出 "Drawing Circle"square.draw();  // 输出 "Drawing Square"
}

底层原理

  • 通过 static_cast 在编译期将基类指针转换为派生类指针,直接调用具体实现。
  • 无虚函数表(vtable)开销,性能更高。

2.2 接口扩展与代码复用
template <typename Derived>
class Addable {
public:Derived operator+(const Derived& other) const {Derived result = static_cast<const Derived&>(*this);result += other;return result;}
};class Vector : public Addable<Vector> {
public:int x, y;Vector(int x, int y) : x(x), y(y) {}Vector& operator+=(const Vector& other) {x += other.x;y += other.y;return *this;}
};int main() {Vector v1(1, 2), v2(3, 4);Vector v3 = v1 + v2;  // 使用基类的 operator+ 实现std::cout << v3.x << ", " << v3.y << std::endl;  // 输出 "4, 6"
}

2.3 对象计数(编译期统计)
template <typename Derived>
class ObjectCounter {
public:static int count;ObjectCounter() { count++; }~ObjectCounter() { count--; }
};template <typename Derived>
int ObjectCounter<Derived>::count = 0;class MyClass : public ObjectCounter<MyClass> {};int main() {MyClass a, b;std::cout << MyClass::count << std::endl;  // 输出 "2"{MyClass c;std::cout << MyClass::count << std::endl;  // 输出 "3"}std::cout << MyClass::count << std::endl;  // 输出 "2"
}

3. CRTP 的底层原理
3.1 模板实例化与类型推导
  • 基类模板参数:在派生类定义时,将自身类型传递给基类模板。
  • 静态类型转换:通过 static_cast 在编译期完成派生类类型的解析。
3.2 符号生成与名称修饰
  • 每个派生类实例化基类模板时,生成独立的符号。
  • 例如:
    • Base<Circle>_4BaseI6CircleE
    • Base<Square>_4BaseI6SquareE

4. CRTP 的优缺点
优点缺点
无虚函数开销,性能更高代码可读性较低,设计复杂度较高
编译期多态,避免运行时类型检查派生类需明确知晓基类模板的实现细节
灵活的类型操作(如运算符重载、静态接口扩展)模板错误信息可能难以调试

5. CRTP 的高级应用
5.1 链式调用(Fluent Interface)
template <typename Derived>
class Chainable {
public:Derived& self() { return static_cast<Derived&>(*this); }Derived& setName(const std::string& name) {self().name = name;return self();}Derived& setValue(int value) {self().value = value;return self();}
};class Config : public Chainable<Config> {
public:std::string name;int value;
};int main() {Config config;config.setName("MyApp").setValue(42);std::cout << config.name << " " << config.value << std::endl; // 输出 "MyApp 42"
}

5.2 编译期策略模式
template <typename Derived>
class SortingPolicy {
public:void sort(int* data, size_t size) {static_cast<Derived*>(this)->sortImpl(data, size);}
};class QuickSort : public SortingPolicy<QuickSort> {
public:void sortImpl(int* data, size_t size) {std::cout << "QuickSort" << std::endl;// 具体实现}
};class MergeSort : public SortingPolicy<MergeSort> {
public:void sortImpl(int* data, size_t size) {std::cout << "MergeSort" << std::endl;// 具体实现}
};int main() {int arr[5] = {5, 3, 1, 4, 2};QuickSort sorter;sorter.sort(arr, 5);  // 输出 "QuickSort"
}

6. 总结
场景技术要点
静态多态通过 static_cast 调用派生类方法,避免虚函数开销
接口扩展基类模板提供通用操作(如 operator+),派生类实现具体逻辑(如 operator+=
编译期对象计数利用静态成员变量统计实例数量
链式调用返回派生类引用实现链式操作
策略模式基类定义策略接口,派生类实现具体策略

CRTP 通过模板继承和编译期类型转换,将多态行为提前到编译期处理,适用于高性能、低延迟场景。合理使用可提升代码复用性和灵活性,但需注意设计复杂度和可维护性。


多选题


题目 1:CRTP 基类如何访问派生类的方法?

以下代码的输出是什么?

template <typename Derived>
class Base {
public:void execute() {static_cast<Derived*>(this)->impl();}
};class Derived : public Base<Derived> {
public:void impl() { std::cout << "Derived impl" << std::endl; }
};int main() {Derived d;d.execute();return 0;
}

A. 输出 Derived impl
B. 编译失败,Base 无法访问 Derivedimpl()
C. 运行时错误,类型转换失败
D. 输出未定义行为


题目 2:静态多态与动态多态的性能对比

以下关于 CRTP 的说法,哪一项正确?

A. CRTP 通过虚函数表实现多态,性能与动态多态相同
B. CRTP 在编译期解析方法调用,性能优于动态多态
C. CRTP 必须在运行时通过 RTTI 检查类型
D. CRTP 的性能不如动态多态,因为模板实例化开销大


题目 3:CRTP 实现对象计数的行为

以下代码的输出是什么?

template <typename Derived>
class Counter {
public:static int count;Counter() { count++; }~Counter() { count--; }
};template <typename Derived>
int Counter<Derived>::count = 0;class WidgetA : public Counter<WidgetA> {};
class WidgetB : public Counter<WidgetB> {};int main() {WidgetA a1, a2;WidgetB b1;std::cout << WidgetA::count << " " << WidgetB::count << std::endl;return 0;
}

A. 2 1
B. 3 1
C. 2 0
D. 1 1


题目 4:CRTP 链式调用的实现

以下代码是否能编译通过?

template <typename Derived>
class Chainable {
public:Derived& setX(int x) {static_cast<Derived*>(this)->x = x;return *static_cast<Derived*>(this);}
};class Point : public Chainable<Point> {
public:int x, y;Point& setY(int y) { this->y = y; return *this; }
};int main() {Point p;p.setX(10).setY(20);return 0;
}

A. 编译成功
B. 编译失败,setX 返回类型错误
C. 编译失败,Point 未继承 Chainable 的正确版本
D. 运行时错误


题目 5:CRTP 与模板特化的交互

以下代码的输出是什么?

template <typename Derived>
class Base {
public:void print() { std::cout << "Base" << std::endl; }
};template <>
class Base<int> {
public:void print() { std::cout << "Base<int>" << std::endl; }
};class Derived : public Base<Derived> {
public:void print() { std::cout << "Derived" << std::endl; }
};int main() {Derived d;d.print();return 0;
}

A. 输出 Base
B. 输出 Derived
C. 输出 Base<int>
D. 编译失败,Derived 无法继承 Base<Derived>



答案与解析


题目 1:CRTP 基类如何访问派生类的方法?

答案:A
解析

  • CRTP 的基类通过 static_cast<Derived*>(this)this 指针转换为派生类指针,直接调用 impl()
  • 选项 B 错误,因为 CRTP 基类与派生类是模板继承关系,编译期即可解析方法调用。

题目 2:静态多态与动态多态的性能对比

答案:B
解析

  • CRTP 的静态多态在编译期完成方法绑定,无需虚函数表查找,性能更高。
  • 选项 A 错误,CRTP 不依赖虚函数表;选项 C 和 D 均与 CRTP 的机制矛盾。

题目 3:CRTP 实现对象计数的行为

答案:A
解析

  • WidgetAWidgetB 分别继承自不同的 Counter 实例化模板,因此它们的静态成员 count 是独立的。
  • WidgetA 构造两次,count 为 2;WidgetB 构造一次,count 为 1。

题目 4:CRTP 链式调用的实现

答案:A
解析

  • Chainable::setX 返回 Derived&(即 Point&),因此 p.setX(10) 返回 Point 对象,支持链式调用 setY(20)
  • 选项 B 错误,返回类型正确;选项 C 和 D 无依据。

题目 5:CRTP 与模板特化的交互

答案:B
解析

  • Derived 继承自 Base<Derived>,未使用 Base<int> 的特化版本。
  • Derived::print() 隐藏了基类的 print(),因此直接调用派生类方法。
  • 选项 C 错误,Derived 的基类是 Base<Derived>,而非 Base<int>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/902704.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小白工具视频转MPG, 功能丰富齐全,无需下载软件,在线使用,超实用

在视频格式转换需求日益多样的今天&#xff0c;小白工具网的在线视频转 MPG 功能https://www.xiaobaitool.net/videos/convert-to-mpg/ &#xff09;脱颖而出&#xff0c;凭借其出色特性&#xff0c;成为众多用户处理视频格式转换的优质选择。 从格式兼容性来看&#xff0c;它支…

银河麒麟系统离线安装nodejs

本篇文章我们介绍如何通过nvm(node版本管理工具)来实现离线安装nodejs 第一步&#xff1a;下载nvm https://github.com/nvm-sh/nvm/releases/tag/v0.40.1 在页面找到【Source code(tar.gz)】下载 第二步&#xff1a;安装nvm 将下载好的tar.gz拷贝到银河麒麟系统文件夹下(加…

Go语言中包导入下划线的作用解析

在Go语言的代码中&#xff0c;有时会看到类似以下的导入语句&#xff1a; import _ "github.com/mattn/go-sqlite3"这种以下划线_开头的导入方式&#xff0c;显得有些特别&#xff0c;尤其是对于新手来说&#xff0c;可能会感到困惑&#xff0c;为什么要这样写&…

Winddows11官网下载安装VMware Workstation Pro17(图文详解)

Winddows11安装VMware17 1、官网下载2、安装3、总结 1、官网下载 官网地址 点击Products&#xff0c;滑到最下面&#xff0c;选择SEE DESKTOPP HYPERVISORS 选择 DOWNLOAD FUSION OR WORKSTATION 自动跳转到下面哪个服界面&#xff0c;注册 输入邮箱地址和图片下面的文字…

DeepSeek智能时空数据分析(二):3秒对话式搞定“等时圈”绘制

序言&#xff1a;时空数据分析很有用&#xff0c;但是GIS/时空数据库技术门槛太高 时空数据分析在优化业务运营中至关重要&#xff0c;然而&#xff0c;三大挑战仍制约其发展&#xff1a;技术门槛高&#xff0c;需融合GIS理论、SQL开发与时空数据库等多领域知识&#xff1b;空…

【Linux网络】应用层自定义协议与序列化及Socket模拟封装

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;博客仓库&#xff1a;https://gitee.com/JohnKingW/linux_test/tree/master/lesson &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &…

基于大模型的结肠癌全病程预测与诊疗方案研究

目录 一、引言 1.1 研究背景与意义 1.2 研究目的与创新点 二、结肠癌概述 2.1 流行病学特征 2.2 发病机制与危险因素 2.3 临床症状与诊断方法 三、大模型技术原理与应用现状 3.1 大模型的基本原理 3.2 在医疗领域的应用情况 3.3 在结肠癌预测中的潜力分析 四、术前…

【UML建模】starUML工具

一.概述 StarUML是一款UML工具&#xff0c;允许用户创建和管理UML&#xff08;统一建模语言&#xff09;模型&#xff0c;广泛应用于软件工程领域。它的主要功能包括创建各种UML图&#xff1a;如用例图、类图、序列图等&#xff0c;支持代码生成与反向工程&#xff0c;以及提供…

模板元编程(Template Metaprogramming, TMP)

C 模板元编程&#xff08;Template Metaprogramming, TMP&#xff09; 模板元编程是一种利用 C 模板系统在 编译期间 完成计算、类型操作和代码生成的编程范式。其核心优势在于通过 零运行时开销 实现高效、类型安全的代码。以下是模板元编程的详细分步解析。 1. 编译时计算 …

Android Build Variants(构建变体)详解

Android Build Variants&#xff08;构建变体&#xff09;是 Android 开发中用于生成不同版本应用程序的一种机制。它允许开发者根据不同的需求&#xff0c;如不同的应用市场、不同的功能模块、不同的环境配置等&#xff0c;从同一个代码库中生成多个不同的 APK。 组成部分 B…

26考研|数学分析:数项级数

数项级数这一章的开始&#xff0c;开启了新的关于“级数”这一新的概念体系的学习进程&#xff0c;此部分共包含四章的内容&#xff0c;分别为数项级数、函数项级数、幂级数以及傅里叶级数。这一章中&#xff0c;首先要掌握级数的相关概念与定义&#xff0c;重难点在于掌握判断…

拥抱健康生活,解锁养生之道

在生活节奏日益加快的当下&#xff0c;健康养生已成为人们关注的焦点。科学的养生方法&#xff0c;能帮助我们增强体质、预防疾病&#xff0c;以更饱满的精神状态拥抱生活。 合理饮食是养生的基石。《黄帝内经》中提到 “五谷为养&#xff0c;五果为助&#xff0c;五畜为益&…

房地产安装工程师简历模板

模板信息 简历范文名称&#xff1a;房地产安装工程师简历模板&#xff0c;所属行业&#xff1a;其他 | 职位&#xff0c;模板编号&#xff1a;XUCP9X 专业的个人简历模板&#xff0c;逻辑清晰&#xff0c;排版简洁美观&#xff0c;让你的个人简历显得更专业&#xff0c;找到好…

HTML5 详细学习笔记

1. HTML5 简介 HTML5 是最新的 HTML 标准&#xff0c;于 2014 年 10 月由 W3C 完成标准制定。它增加了许多新特性&#xff0c;包括语义化标签、多媒体支持、图形效果、离线存储等。 1.1 HTML5 文档基本结构 <!DOCTYPE html> <html lang"zh-CN"> <h…

【网络入侵检测】基于Suricata源码分析NFQ IPS模式实现

【作者主页】只道当时是寻常 【专栏介绍】Suricata入侵检测。专注网络、主机安全,欢迎关注与评论。 1. 概要 👋 本文聚焦于 Suricata 7.0.10 版本源码,深入剖析其 NFQ(Netfilter Queue)模式的实现原理。通过系统性拆解初始化阶段的配置流程、数据包监听机制的构建逻辑,以…

C语言结构体和union内存对齐

在C语言的世界里&#xff0c;结构体&#xff08;struct&#xff09;和联合体&#xff08;union&#xff09;的内存布局一直是困扰许多开发者的难题。当我们定义一个结构体时&#xff0c;编译器会按照特定的规则为每个成员分配内存空间&#xff0c;这些规则被称为内存对齐。看似…

本地部署DeepSeek-R1模型接入PyCharm

以下是DeepSeek-R1本地部署及接入PyCharm的详细步骤指南,整合了视频内容及官方文档核心要点: 一、本地部署DeepSeek-R1模型 1. 安装Ollama框架 ​下载安装包 访问Ollama官网(https://ollama.com/download)Windows用户选择.exe文件,macOS用户选择.dmg包。 ​安装验证 双击…

IEEE综述 | 车道拓扑推理20年演进:从程序化建模到车载传感器

导读 车道拓扑推理对于高精建图和自动驾驶应用至关重要&#xff0c;从早期的程序化建模方法发展到基于车载传感器的方法&#xff0c;但是很少有工作对车道拓扑推理技术进行全面概述。为此&#xff0c;本文系统性地调研了车道拓扑推理技术&#xff0c;同时确定了未来研究的挑战和…

开源模型应用落地-语音合成-MegaTTS3-零样本克隆与多语言生成的突破

一、前言 在人工智能技术飞速发展的今天,文本转语音(TTS)技术正以前所未有的速度改变着人机交互的方式。近日,字节跳动与浙江大学联合推出了一款名为MegaTTS3 的开源TTS模型,再次刷新了行业对高质量语音合成的认知。作为一款轻量化设计的模型,MegaTTS3以仅0.45亿参数 的规…

Python爬虫实战:移动端逆向工具Fiddler经典案例

一、引言 在移动互联网迅猛发展的当下,移动端应用产生了海量的数据。对于开发者而言,获取这些数据对于市场调研、竞品分析、数据挖掘等工作具有重要意义。Fiddler 作为一款功能强大的 Web 调试代理工具,能够有效捕获、分析和修改移动端的网络请求,为开发者深入了解移动端网…