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.pswp.cn/news/902704.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/902704.shtml
英文地址,請注明出處:http://en.pswp.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 調試代理工具,能夠有效捕獲、分析和修改移動端的網絡請求,為開發者深入了解移動端網…