第十四章:模板實例化_《C++ Templates》notes

模板實例化

      • 核心知識點解析
      • 多選題
      • 設計題
      • 關鍵點總結

核心知識點解析

兩階段查找(Two-Phase Lookup)
原理
模板在編譯時分兩個階段處理:

  1. 第一階段(定義時):檢查模板語法和非依賴名稱(Non-dependent Names),此時不依賴模板參數。
  2. 第二階段(實例化時):檢查依賴名稱(Dependent Names),并綁定到具體類型。

代碼示例

#include <iostream>template<typename T>
void foo(T t) {// 非依賴名稱:在編譯時檢查bar(t);  // 第一階段查找bar(),若未聲明則報錯// 依賴名稱:在第二階段實例化時查找t.func(); 
}void bar(int) { std::cout << "bar(int)\n"; }struct Data {void func() const { std::cout << "Data::func()\n"; }
};int main() {foo(42);     // 實例化foo<int>,調用bar(int)和Data::func()return 0;
}

測試用例

// 輸出:
// bar(int)
// Data::func()

  1. 實例化點(Point of Instantiation, POI)
    原理
    模板實例化的位置由POI決定,通常位于首次使用模板的最近命名空間作用域之后。

代碼示例

template<typename T>
void baz() { /* ... */ }void test() {baz<int>();  // 觸發baz<int>的實例化
}// POI位于test()之后,此處可訪問baz<int>

測試用例

// 正確:POI在test()之后

  1. 顯式實例化(Explicit Instantiation)
    用途
    手動控制模板的實例化位置,避免重復實例化。

代碼示例

template<typename T>
T add(T a, T b) { return a + b; }// 顯式實例化int版本
template int add<int>(int, int);int main() {add(1, 2);   // 使用顯式實例化的版本return 0;
}

測試用例

// 輸出:3

  1. 編譯期if constexpr(C++17)
    原理
    在編譯期根據條件選擇代碼分支,未使用的代碼會被丟棄。

代碼示例

template<typename T>
auto get_value(const T& t) {if constexpr (std::is_pointer_v<T>) {return *t;} else {return t;}
}int main() {int x = 5;int* p = &x;std::cout << get_value(x) << "\n";   // 輸出5std::cout << get_value(p) << "\n";   // 輸出5return 0;
}

測試用例

// 輸出:
// 5
// 5

綜合測試程序
以下將所有知識點整合到一個測試程序中:

#include <iostream>
#include <type_traits>// 兩階段查找示例
template<typename T>
void foo(T t) {bar(t);          // 非依賴名稱,第一階段需可見t.func();        // 依賴名稱,第二階段實例化時查找
}void bar(int) { std::cout << "bar(int)\n"; }struct Data {void func() const { std::cout << "Data::func()\n"; }
};// 顯式實例化示例
template<typename T>
T add(T a, T b) { return a + b; }template int add<int>(int, int);  // 顯式實例化int版本// 編譯期if constexpr示例
template<typename T>
auto get_value(const T& t) {if constexpr (std::is_pointer_v<T>) {return *t;} else {return t;}
}int main() {// 測試兩階段查找foo(42);     // 調用bar(int)和Data::func()// 測試顯式實例化std::cout << add(3, 5) << "\n";  // 輸出8// 測試編譯期if constexprint x = 10;int* p = &x;std::cout << get_value(x) << "\n";   // 輸出10std::cout << get_value(p) << "\n";   // 輸出10return 0;
}

輸出結果

bar(int)
Data::func()
8
10
10

多選題

題目1:模板實例化的基本流程是?
A. 解析模板定義時立即生成代碼
B. 使用時按需生成代碼(按需實例化)
C. 顯式實例化指令觸發代碼生成
D. 所有模板參數必須顯式指定

答案:B, C
詳解

  • B 正確:C++ 默認采用按需實例化,僅在用到模板時生成代碼。
  • C 正確:通過 template class MyClass<int>; 可顯式觸發實例化。
  • A 錯誤:模板定義時不會立即生成代碼。
  • D 錯誤:模板參數可通過推導自動確定。

題目2:兩階段查找(Two-Phase Lookup)的規則是?
A. 非依賴名稱在模板定義時解析
B. 依賴名稱在模板實例化時解析
C. 所有名稱都在實例化時解析
D. ADL 僅在實例化階段生效

答案:A, B
詳解

  • A 正確:非依賴名稱(如普通函數名)在模板定義時解析。
  • B 正確:依賴名稱(如 T::func)在實例化時結合實參類型解析。
  • C 錯誤:非依賴名稱提前解析。
  • D 錯誤:ADL 在兩階段均可能生效。

題目3:顯式實例化聲明(extern template)的作用是?
A. 防止模板在當前翻譯單元實例化
B. 強制模板在其他地方實例化
C. 減少編譯時間
D. 提升鏈接效率

答案:A, C
詳解

  • A 正確:阻止隱式實例化,避免重復代碼生成。
  • C 正確:通過集中實例化減少編譯負擔。
  • B 錯誤:僅聲明不實現,無法強制實例化位置。
  • D 錯誤:鏈接效率取決于實現,非主要目的。

題目4:編譯期if constexpr(C++17)與運行期if的關鍵區別是?
A. 編譯期分支可能被完全剔除
B. 運行期if可處理非constexpr條件
C. 編譯期if必須滿足常量表達式
D. 兩者均可用于模板元編程

答案:A, B, C
詳解

  • A 正確:未選擇的分支代碼會被丟棄。
  • B 正確:運行期if無此限制。
  • C 正確:constexpr if條件需在編譯期可求值。
  • D 錯誤:運行期if無法參與模板特化。

題目5:顯式實例化與顯式特化的區別是?
A. 顯式實例化生成通用代碼
B. 顯式特化為特定模式提供定制實現
C. 顯式實例化優先級高于顯式特化
D. 顯式特化需在命名空間作用域聲明

答案:A, B
詳解

  • A 正確:template class MyClass<int>; 生成MyClass<int>的代碼。
  • B 正確:template<> void MyClass<int>::func() {...} 定制int版本的實現。
  • C 錯誤:顯式特化優先于顯式實例化。
  • D 錯誤:顯式特化需在全局或類作用域聲明。

題目6:以下哪種情況會導致模板實例化失敗?
A. 成員函數模板推導失敗
B. 構造函數默認參數未定義
C. 虛函數表生成時依賴未實例化的類型
D. 靜態成員變量未顯式初始化

答案:A, B, C
詳解

  • A 正確:成員函數模板推導失敗會導致實例化中止。
  • B 正確:構造函數默認參數若依賴未實例化的類型會失敗。
  • C 正確:虛表生成需要完整類型信息。
  • D 錯誤:靜態成員可在類外延遲初始化。

題目7:類模板成員的顯式實例化方式是?
A. template void MyClass<int>::func();
B. template MyClass<int>::func();
C. extern template void MyClass<int>::func();
D. template<> void MyClass<int>::func();

答案:A
詳解

  • A 正確:顯式實例化成員函數的語法。
  • B 錯誤:缺少template關鍵字。
  • C 錯誤:extern用于聲明而非定義。
  • D 錯誤:這是顯式特化的語法。

題目8:編譯期if constexpr的典型應用場景是?
A. 實現類型萃取(Type Traits)
B. 條件編譯不同代碼路徑
C. 優化遞歸模板展開
D. 替代宏定義

答案:A, B, C
詳解

  • A 正確:通過條件判斷篩選類型特性。
  • B 正確:根據常量條件選擇執行路徑。
  • C 正確:避免無效分支的代碼膨脹。
  • D 錯誤:constexpr if無法完全替代宏的語義。

題目9:模板實例化的存儲優化技術包括?
A. 鏈接器去重(Linker Deduplication)
B. 內聯展開(Inlining)
C. 空基類優化(EBO)
D. 全局變量合并

答案:A, B
詳解

  • A 正確:鏈接器消除重復實例化的代碼。
  • B 正確:小函數可能被內聯以避免實例化。
  • C 錯誤:EBO與模板實例化無關。
  • D 錯誤:全局變量合并不適用于模板。

題目10:以下代碼的輸出是?

template<typename T> void foo(T) { cout << "T" << endl; }
template<> void foo<int>(int) { cout << "int" << endl; }
extern template void foo<double>(double);int main() {foo(1);    // Afoo(1.0);  // Bfoo('c');  // C
}

A. int
B. T
C. T

答案:A. int, B. T, C. T
詳解

  • A 調用顯式特化版本。
  • B 未顯式實例化double,按需實例化通用版本。
  • C 字符字面量推導為char,調用通用版本。

設計題

題目1:實現一個線程安全的單例模式,要求支持任意類型T,并利用顯式實例化優化性能。

#include <iostream>
#include <mutex>template<typename T>
class Singleton {
private:static T* instance;static std::once_flag flag;Singleton() = default;~Singleton() = default;public:Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;static T& getInstance() {std::call_once(flag, []{instance = new T();});return *instance;}
};// 顯式實例化常用類型以優化性能
template<> Singleton<int>::instance = nullptr;
template<> std::once_flag Singleton<int>::flag;int main() {Singleton<int>& si = Singleton<int>::getInstance();Singleton<std::string>& ss = Singleton<std::string>::getInstance();return 0;
}

題目2:編寫一個模板元函數is_pointer_v,檢測類型是否為指針,并利用編譯期if constexpr優化性能。

#include <iostream>
#include <type_traits>template<typename T>
constexpr bool is_pointer_v = false;template<typename T>
constexpr bool is_pointer_v<T*> = true;template<typename T>
void checkPointer(T val) {if constexpr (is_pointer_v<T>) {std::cout << "Pointer type" << std::endl;} else {std::cout << "Non-pointer type" << std::endl;}
}int main() {int a;int* p = &a;checkPointer(a);  // 輸出 Non-pointer typecheckPointer(p);  // 輸出 Pointer typereturn 0;
}

題目3:實現一個泛型緩存類Cache,支持通過鍵值快速訪問對象,利用顯式實例化減少模板代碼膨脹。

#include <unordered_map>
#include <string>template<typename Key, typename Value>
class Cache {
private:std::unordered_map<Key, Value> storage;public:void set(const Key& key, const Value& value) {storage[key] = value;}Value get(const Key& key) {return storage[key];}
};// 顯式實例化常用組合
template class Cache<std::string, int>;
template class Cache<int, std::string>;int main() {Cache<std::string, int> intCache;intCache.set("age", 25);std::cout << intCache.get("age") << std::endl;  // 輸出 25Cache<int, std::string> strCache;strCache.set(1, "one");std::cout << strCache.get(1) << std::endl;  // 輸出 onereturn 0;
}

題目4:設計一個支持多種序列化協議的模板類Serializer,利用顯式實例化適配不同協議。

#include <iostream>
#include <string>enum class Protocol { JSON, XML };template<Protocol P>
class Serializer {
public:static std::string serialize(int value) {if constexpr (P == Protocol::JSON) {return "{\"value\":" + std::to_string(value) + "}";} else {return "<value>" + std::to_string(value) + "</value>";}}
};// 顯式實例化常用協議
template class Serializer<Protocol::JSON>;
template class Serializer<Protocol::XML>;int main() {std::cout << Serializer<Protocol::JSON>::serialize(42) << std::endl;  // 輸出 {"value":42}std::cout << Serializer<Protocol::XML>::serialize(42) << std::endl;   // 輸出 <value>42</value>return 0;
}

題目5:實現一個類型萃取工具TypeTraits,利用編譯期if constexpr簡化類型判斷邏輯。

#include <iostream>
#include <type_traits>template<typename T>
struct TypeTraits {static constexpr bool is_pointer = false;static constexpr bool is_reference = false;
};template<typename T>
struct TypeTraits<T*> {static constexpr bool is_pointer = true;
};template<typename T>
struct TypeTraits<T&> {static constexpr bool is_reference = true;
};template<typename T>
void analyzeType(const T& value) {if constexpr (TypeTraits<T>::is_pointer) {std::cout << "Pointer type" << std::endl;} else if constexpr (TypeTraits<T>::is_reference) {std::cout << "Reference type" << std::endl;} else {std::cout << "Value type" << std::endl;}
}int main() {int a = 5;int* p = &a;int& r = a;analyzeType(a);  // 輸出 Value typeanalyzeType(p);  // 輸出 Pointer typeanalyzeType(r);  // 輸出 Reference typereturn 0;
}

代碼測試說明

  1. 編譯命令:使用支持C++17的編譯器(如GCC 7+、Clang 5+):
    g++ -std=c++17 -o test test.cpp && ./test
    
  2. 測試要點
    • 每個設計題的main函數均包含測試用例。
    • 多選題答案需結合書中第十四章的實例化機制、兩階段查找規則等知識點驗證。

關鍵點總結

  1. 兩階段查找:確保模板定義時非依賴名稱可見,依賴名稱在實例化時解析。
  2. 顯式實例化:通過template關鍵字手動實例化,減少編譯開銷。
  3. if constexpr:編譯期分支選擇,優化生成的代碼。

通過以上示例和測試,可以深入理解C++模板實例化的機制和優化技巧。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/74306.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/74306.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/74306.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

LSM-Tree(Log-Structured Merge-Tree)詳解

1. 什么是 LSM-Tree? LSM-Tree(Log-Structured Merge-Tree)是一種 針對寫優化的存儲結構,廣泛用于 NoSQL 數據庫(如 LevelDB、RocksDB、HBase、Cassandra)等系統。 它的核心思想是: 寫入時只追加寫(Append-Only),將數據先寫入內存緩沖區(MemTable)。內存數據滿后…

LangChain組件Tools/Toolkits詳解(6)——特殊類型注解Annotations

LangChain組件Tools/Toolkits詳解(6)——特殊類型注解Annotations 本篇摘要14. LangChain組件Tools/Toolkits詳解14.6 特殊類型注解Annotations14.6.1 特殊類型注解分類14.6.1 InjectedToolArg構建運行時綁定值工具14.6.3 查看并傳入參數14.6.4 在運行時注入參數14.6.5 其它特…

openharmony中hilog實證記錄說明(3.1和5.0版本)

每次用這個工具hilog都有一些小用法記不清&#xff0c;需要花一些時間去查去分析使用方法&#xff0c;為了給豐富多彩的生活留出更多的時間&#xff0c;所以匯總整理共享來了&#xff0c;它來了它來了~~~~~~~~~ 開始是想通過3.1來匯總的&#xff0c;但實際測試發現openharmony…

NVIDIA nvmath-python:高性能數學庫的Python接口

NVIDIA nvmath-python&#xff1a;高性能數學庫的Python接口 NVIDIA nvmath-python是一個高性能數學庫的Python綁定&#xff0c;它為Python開發者提供了訪問NVIDIA優化數學算法的能力。這個庫特別適合需要高性能計算的科學計算、機器學習和數據分析應用。 文章目錄 NVIDIA nv…

【euclid】20 2D包圍盒模塊(box2d.rs)

box2d.rs文件定義了一個二維軸對齊矩形&#xff08;Box2D&#xff09;&#xff0c;使用最小和最大坐標來表示。矩形在坐標類型&#xff08;T&#xff09;和單位&#xff08;U&#xff09;上是泛型的。代碼提供了多種方法來操作和查詢矩形&#xff0c;包括求交集、并集、平移、縮…

ChatTTS 開源文本轉語音模型本地部署 API 使用和搭建 WebUI 界面

ChatTTS&#xff08;Chat Text To Speech&#xff09;&#xff0c;專為對話場景設計的文本生成語音(TTS)模型&#xff0c;適用于大型語言模型(LLM)助手的對話任務&#xff0c;以及諸如對話式音頻和視頻介紹等應用。支持中文和英文&#xff0c;還可以穿插笑聲、說話間的停頓、以…

鏈表相關知識總結

1、數據結構 基本概念&#xff1a; 數據項&#xff1a;一個數據元素可以由若干個數據項組成數據對象&#xff1a;有相同性質的數據元素的集合&#xff0c;是數據的子集數據結構&#xff1a;是相互之間存在一種或多種特定關系的數據元素的集合 邏輯結構和物理結構&#xff1a…

藍橋杯備考-》單詞接龍

很明顯&#xff0c;這道題是可以用DFS來做的&#xff0c;我們直接暴力搜索&#xff0c;但是這里有很多點是我們需要注意的。 1.我們如何確定兩個單詞能接上&#xff1f; 比如touch和choose 應該合成為touchoose 就是這樣兩個單詞&#xff0c;我們讓一個指針指著第一個字符串…

C語言-訪問者模式詳解與實踐

C語言訪問者模式詳解與實踐 - 傳感器數據處理系統 1. 什么是訪問者模式&#xff1f; 在嵌入式系統中&#xff0c;我們經常需要對不同傳感器的數據進行多種處理&#xff0c;如數據校準、過濾、存儲等。訪問者模式允許我們在不修改傳感器代碼的情況下&#xff0c;添加新的數據處…

(UI自動化測試web端)第二篇:元素定位的方法_xpath路徑定位

1、第一種xpath路徑定位&#xff1a; 絕對路徑&#xff1a;表達式是以 /html開頭&#xff0c;元素的層級之間是以 / 分隔相同層級的元素可以使用下標&#xff0c;下標是從1開始的需要列出元素所經過的所有層級元素&#xff0c;工作當中一般不使用絕對路徑 例&#xff1a;/html/…

設置GeoJSONVectorTileLayer中的line填充圖片

設置GeoJSONVectorTileLayer中的line填充圖片 關鍵&#xff1a;linePatternFile const style [{filter: true,renderPlugin: {dataConfig: {type: "line",},type: "line",},symbol: {linePatternFile: "http://examples.maptalks.com/resources/pat…

electron框架(4.0)electron-builde和electron Forge的打包方式

----使用electron-builder打包&#xff08;需要魔法&#xff09; --安裝electron-builder: npm install electron-builder -D--package.json中進行相關配置&#xff1a; {"name": "video-tools","version": "1.0.0","main&quo…

讓 MGR 不從 Primary 的節點克隆數據?

問題 MGR 中&#xff0c;新節點在加入時&#xff0c;為了與組內其它節點的數據保持一致&#xff0c;它會首先經歷一個分布式恢復階段。在這個階段&#xff0c;新節點會隨機選擇組內一個節點&#xff08;Donor&#xff09;來同步差異數據。 在 MySQL 8.0.17 之前&#xff0c;同…

第三十二篇 深入解析Kimball維度建模:構建企業級數據倉庫的完整框架

目錄 一、維度建模設計原則深度剖析1.1 業務過程驅動設計1.2 星型模式VS雪花模式 二、維度建模五步法實戰&#xff08;附完整案例&#xff09;2.1 業務需求映射2.2 模型詳細設計2.3 緩慢變化維處理 三、高級建模技術解析3.1 漸變維度橋接表3.2 快照事實表設計 四、性能優化體系…

IntelliJ IDEA 中 Maven 的 `pom.xml` 變灰帶橫線?一文詳解解決方法

前言 在使用 IntelliJ IDEA 進行 Java 開發時&#xff0c;如果你發現項目的 pom.xml 文件突然變成灰色并帶有刪除線&#xff0c;這可能是 Maven 的配置或項目結構出現了問題。 一、問題現象與原因分析 現象描述 文件變灰&#xff1a;pom.xml 在項目資源管理器中顯示為灰色。…

緩存過期時間之邏輯過期

1. 物理不過期&#xff08;Physical Non-Expiration&#xff09; 定義&#xff1a;在Redis中不設置EXPIRE時間&#xff0c;緩存鍵永久存在&#xff08;除非主動刪除或內存淘汰&#xff09;。目的&#xff1a;徹底規避因緩存自動過期導致的擊穿&#xff08;單熱點失效&#xff…

基于WebAssembly的瀏覽器密碼套件

目錄 一、前言二、WebAssembly與瀏覽器密碼套件2.1 WebAssembly技術概述2.2 瀏覽器密碼套件的需求三、系統設計思路與架構3.1 核心模塊3.2 系統整體架構圖四、核心數學公式與算法證明4.1 AES-GCM加解密公式4.2 SHA-256哈希函數五、異步任務調度與GPU加速設計5.1 異步任務調度5.…

Qt的內存管理機制

在Qt中&#xff0c;顯式使用new創建的對象通常不需要顯式調用delete來釋放內存&#xff0c;這是因為Qt提供了一種基于對象樹(Object Tree)和父子關系(Parent-Child Relationship)的內存管理機制。這種機制可以自動管理對象的生命周期&#xff0c;確保在適當的時候釋放內存&…

數據結構之雙向鏈表-初始化鏈表-頭插法-遍歷鏈表-獲取尾部結點-尾插法-指定位置插入-刪除節點-釋放鏈表——完整代碼

數據結構之雙向鏈表-初始化鏈表-頭插法-遍歷鏈表-獲取尾部結點-尾插法-指定位置插入-刪除節點-釋放鏈表——完整代碼 #include <stdio.h> #include <stdlib.h>typedef int ElemType;typedef struct node{ElemType data;struct node *next, *prev; }Node;//初化鏈表…

【Linux網絡-五種IO模型與阻塞IO】

一、引入 網絡通信的本質就是進程間的通信&#xff0c;進程間通信的本質就是IO&#xff08;Input&#xff0c;Output&#xff09; I/O&#xff08;input/output&#xff09;也就是輸入和輸出&#xff0c;在馮諾依曼體系結構當中&#xff0c;將數據從輸入設備拷貝到內存就叫作…