第十三章 : Names in Templates_《C++ Templates》notes

Names in Templates

      • 重難點
      • 多選題
      • 設計題


重難點

1. 名稱分類與基本概念
知識點:

  • 限定名(Qualified Name):使用::.顯式指定作用域的名稱(如std::vector
  • 非限定名(Unqualified Name):不帶作用域限定的名稱(如vector
  • 依賴名(Dependent Name):依賴于模板參數的名稱(如T::value_type
  • 非依賴名(Non-dependent Name):不依賴模板參數的名稱(如int

測試用例:

#include <iostream>
#include <vector>template<typename T>
void foo() {T::value_type x;  // 依賴名(T未實例化前無法確定是否合法)
}int main() {foo<std::vector<int>>();  // 實例化時檢查T::value_type合法性return 0;
}

2. 依賴名與非依賴名的查找規則
知識點:

  • 非限定名查找
    • 普通查找(Ordinary Lookup):在模板定義時查找所有可見的非依賴名
    • ADL(Argument-Dependent Lookup):在模板實例化時查找關聯命名空間
  • 限定名查找
    • 直接在當前作用域鏈中查找,不觸發ADL

示例代碼:

namespace NS {struct S {};void bar(S) { std::cout << "NS::bar\n"; }
}template<typename T>
void baz(T t) {bar(t);  // 非限定名:普通查找+ADL
}int main() {NS::S s;baz(s);  // 調用NS::bar(通過ADL)return 0;
}

3. 注入類名(Injected Class Name)
知識點:

  • 類模板內部可以隱式使用類名作為模板名(無需<T>
  • 在派生類中可通過Base::Base訪問基類模板

示例代碼:

#include <iostream>// 基類模板 Base
template<typename T>
class Base {
public:// 定義一個類型別名 type,其類型為模板參數 Tusing type = T;
};// 派生類模板 Derived,繼承自 Base<T>
template<typename T>
class Derived : public Base<T> {
public:// 派生類的構造函數Derived() {// 顯式使用完整的基類模板實例化形式typename Base<T>::type x;// 為了讓代碼更完整,我們可以給 x 賦值并輸出x = static_cast<T>(10);std::cout << "Value of x: " << x << std::endl;}
};int main() {// 實例化 Derived 類模板,模板參數為 intDerived<int> d;return 0;
}    

4. 友元模板(Friend Templates)
知識點:

  • 友元可以是函數模板、類模板或成員模板
  • 友元聲明需在類外定義時顯式指定模板參數

示例代碼:

template<typename T>
class MyClass {friend void helper<>(MyClass<T>&);  // 友元函數模板
};template<typename T>
void helper(MyClass<T>& obj) {obj.value = 42;
}int main() {MyClass<int> obj;helper(obj);return 0;
}

5. 兩階段查找(Two-Phase Lookup)
知識點:

  • 第一階段(定義時):檢查非依賴名,忽略模板參數
  • 第二階段(實例化時):檢查依賴名,觸發ADL

常見陷阱示例:

template<typename T>
void foo() {bar();  // 第一階段查找bar,若未找到則報錯
}namespace NS {void bar() {}
}int main() {foo<NS::Bar>();  // 錯誤!第一階段未找到bar()return 0;
}

6. 代碼測試與調試技巧
測試策略:

  1. 分階段編譯:先編譯模板定義,再實例化觀察錯誤
  2. 顯式實例化:通過template class MyClass<int>;強制實例化
  3. 使用static_assert:在模板中添加靜態斷言驗證條件

示例測試代碼:

template<typename T>
class MyClass {static_assert(std::is_integral_v<T>, "T must be integral");
};int main() {MyClass<int> ok;       // 通過// MyClass<double> error;  // 編譯失敗(靜態斷言)return 0;
}

多選題

題目1
關于依賴名稱的查找規則,以下哪些說法正確?
A. 非限定依賴名稱在第二階段通過ADL查找
B. 限定依賴名稱直接在第一階段查找
C. 成員模板中的依賴名稱自動視為模板
D. typename關鍵字只能用于非限定依賴名稱前

答案
A, C
詳解
A正確:非限定依賴名稱在第二階段通過ADL查找
C正確:成員模板中的依賴名稱需用template關鍵字顯式指明

題目2
關于ADL的適用場景,以下哪些是正確的?
A. 查找非成員函數時參數類型所屬的命名空間
B. 查找成員函數的基類鏈
C. 查找模板參數類型的嵌套類型
D. 查找全局作用域的函數

答案
A, C
詳解
A正確:ADL通過參數類型所屬命名空間查找函數
C正確:ADL會查找參數類型的嵌套類型

題目3
關于注入類名稱,以下哪些描述正確?
A. 在類模板內部可直接使用未限定類名
B. 注入名稱優先于外部同名函數
C. 可用于訪問基類的成員
D. 實例化后指向具體模板實例

答案
A, D
詳解
A正確:類模板內部可直接用MyClass代替MyClass<T>
D正確:注入名稱在實例化后指向具體實例類型

題目4
關于名稱查找階段,以下哪些正確?
A. 非依賴名稱僅在第一階段查找
B. 依賴名稱僅在第二階段查找
C. 友元聲明影響第二階段查找
D. using聲明影響第一階段查找

答案
A, D
詳解
A正確:非限定非依賴名稱在第一階段完成查找
D正確:using聲明會向第一階段作用域引入名稱

題目5
關于模板參數作用域,以下哪些正確?
A. 模板參數作用域從聲明處開始
B. 模板參數可隱藏外層作用域名稱
C. 類模板參數作用域包含成員定義
D. 函數模板參數作用域包含默認實參

答案
A, B, C
詳解
A正確:模板參數作用域起始于聲明處
B正確:模板參數會隱藏外層同名名稱
C正確:類模板參數作用域覆蓋成員定義

題目6
關于友元聲明的名稱查找,以下哪些正確?
A. 友元函數聲明影響普通名稱查找
B. 友元類聲明參與ADL查找
C. 顯式友元模板影響第二階段查找
D. 友元聲明必須在使用前可見

答案
B, C
詳解
B正確:友元類參與ADL查找路徑
C正確:顯式友元模板在第二階段被考慮

題目7
關于using聲明在模板中的作用,以下哪些正確?
A. 引入命名空間成員到模板作用域
B. 可用于解除名稱隱藏
C. 必須在模板定義體外聲明
D. 影響第一階段名稱查找

答案
A, B, D
詳解
A正確:using可將命名空間成員引入當前作用域
B正確:可解除外層同名名稱的隱藏
D正確:using聲明影響第一階段的普通查找

題目8
關于當前實例化的判斷,以下哪些情況成立?
A. 直接使用未限定的類模板名
B. 訪問成員模板的嵌套類型
C. 使用this->限定成員訪問
D. 通過typename限定依賴類型

答案
A, B
詳解
A正確:直接使用類模板名指向當前實例
B正確:成員模板的嵌套類型屬于當前實例

題目9
關于兩階段查找的例外,以下哪些正確?
A. 非類型模板參數的默認實參在第二階段處理
B. 虛函數表在第二階段初始化
C. 默認成員初始化器在第一階段處理
D. 異常規格不在兩階段處理范圍內

答案
C, D
詳解
C正確:默認成員初始化器在第一階段處理
D正確:異常規格不屬于兩階段處理范圍

題目10
關于模板特化與名稱查找的關系,以下哪些正確?
A. 顯式特化不影響第一階段查找
B. 偏特化參與第二階段ADL查找
C. 全局特化優先于隱式實例化
D. 特化中的名稱獨立于主模板作用域

答案
A, C
詳解
A正確:顯式特化僅在實例化時被選擇
C正確:顯式全局特化優先于隱式實例化


設計題

題目1
設計一個模板類Logger,要求:

  • 支持日志級別(DEBUG/INFO/WARNING)
  • 使用ADL查找自定義日志處理器
  • 提供默認處理器輸出到std::cout
  • 測試用例需驗證ADL查找和默認行為

答案

#include <iostream>
#include <string>// 主模板
template<typename T>
class Logger {
public:void log(const std::string& msg) {handle_log(msg); // ADL查找自定義處理器}
};// 默認處理器(通過ADL查找)
void handle_log(const std::string& msg) {std::cout << "[DEFAULT] " << msg << std::endl;
}// 自定義處理器示例
namespace CustomLog {struct Handler {static void handle(const std::string& msg) {std::cerr << "[CUSTOM] " << msg << std::endl;}};// ADL輔助函數void handle_log(const std::string& msg) {Handler::handle(msg);}
}// 測試用例
int main() {Logger<int> logger1;logger1.log("Hello"); // 調用默認處理器Logger<CustomLog::Handler> logger2;logger2.log("World"); // 調用自定義處理器return 0;
}

題目2
實現一個依賴名稱查找的智能指針模板,要求:

  • 支持自定義刪除器
  • 刪除器通過依賴名稱查找
  • 默認刪除器使用delete
  • 測試用例需驗證自定義刪除器和默認行為

答案

template<typename T, typename Deleter = void>
class SmartPtr {T* ptr;
public:SmartPtr(T* p) : ptr(p) {}~SmartPtr() {delete_ptr(ptr); // 依賴名稱查找}private:// 依賴名稱:通過ADL查找Deleter::delete_ptrvoid delete_ptr(T* p) {Deleter::delete_ptr(p);}
};// 默認刪除器
struct DefaultDeleter {static void delete_ptr(void* p) {::delete static_cast<int*>(p);}
};// 自定義刪除器
struct FileDeleter {static void delete_ptr(FILE* p) {fclose(p);}
};// 測試用例
int main() {SmartPtr<int> ptr1(new int(42)); // 使用默認刪除器SmartPtr<FILE, FileDeleter> ptr2(fopen("test.txt", "w")); // 使用自定義刪除器return 0;
}

題目3
設計一個支持注入類名稱的模板元編程工具,要求:

  • 提供類型特征檢測接口
  • 注入類名稱簡化成員訪問
  • 測試用例驗證注入名稱和普通成員訪問一致性

答案

template<typename T>
struct TypeTraits {// 注入類名稱簡化成員訪問using value_type = typename T::value_type;using iterator = typename T::iterator;static constexpr bool has_size = requires(T t) {{ t.size() } -> std::convertible_to<std::size_t>;};
};// 測試容器
template<typename T>
struct MyContainer {using value_type = T;using iterator = T*;std::size_t size() const { return 0; }
};// 測試用例
int main() {static_assert(TypeTraits<MyContainer<int>>::has_size);static_assert(std::is_same_v<TypeTraits<MyContainer<int>>::value_type, int>);return 0;
}

題目4
實現一個支持當前實例化的模板偏特化檢測器,要求:

  • 判斷給定類型是否為當前實例化
  • 使用this->限定成員訪問
  • 測試用例驗證檢測邏輯

答案

template<typename T>
struct IsCurrentInstantiation {static constexpr bool value = false;
};template<typename T>
struct MyClass {template<typename U>struct Inner {static constexpr bool is_current = std::is_same_v<U, MyClass<T>::Inner>; // 當前實例化檢測};
};// 測試用例
int main() {MyClass<int>::Inner<double> inner;static_assert(inner.is_current);return 0;
}

題目5
設計一個結合ADL和模板參數作用域的工具,要求:

  • 自動注冊類型到工廠類
  • 使用ADL查找注冊函數
  • 測試用例驗證多命名空間注冊

答案

#include <map>
#include <string>// 類型注冊工廠
template<typename Key>
class Registry {std::map<Key, std::string> registry;
public:template<typename T>void register_type(const std::string& name) {registry[name] = typeid(T).name(); // 依賴名稱查找}void list_types() const {for (const auto& [name, type] : registry) {std::cout << name << " -> " << type << std::endl;}}
};// ADL注冊函數
namespace NS1 {struct TypeA {};void register_type(Registry<TypeA>& reg, const std::string& name) {reg.register_type<TypeA>(name);}
}namespace NS2 {struct TypeB {};void register_type(Registry<TypeB>& reg, const std::string& name) {reg.register_type<TypeB>(name);}
}// 測試用例
int main() {Registry<TypeA> reg_a;NS1::register_type(reg_a, "TypeA");Registry<TypeB> reg_b;NS2::register_type(reg_b, "TypeB");reg_a.list_types();reg_b.list_types();return 0;
}

測試用例執行說明

  1. 編譯命令(使用C++17標準):
    g++ -std=c++17 -o test test.cpp && ./test
    
  2. 預期輸出
    • 題目1輸出:
      [DEFAULT] Hello
      [CUSTOM] World
      
    • 題目2輸出(無輸出,程序正常退出)
    • 題目3輸出(無輸出,靜態斷言通過)
    • 題目4輸出(無輸出,靜態斷言通過)
    • 題目5輸出:
      TypeA -> St4TypeA
      TypeB -> St4TypeB
      

設計要點說明

  1. ADL機制:通過自定義命名空間和輔助函數實現日志處理器的動態選擇
  2. 依賴名稱delete_ptr方法通過依賴名稱查找實現自定義刪除邏輯
  3. 注入類名稱TypeTraits直接使用T::value_type簡化成員訪問
  4. 當前實例化檢測:利用this->限定符實現模板偏特化的運行時檢測
  5. 多命名空間注冊:通過ADL在不同命名空間中注冊類型到統一工廠

總結與學習路徑

  1. 理解名稱分類:區分依賴/非依賴名、限定/非限定名
  2. 掌握查找規則:普通查找 vs ADL,兩階段查找機制
  3. 實踐友元與注入類名:通過代碼示例熟悉語法
  4. 調試技巧:利用靜態斷言和分階段編譯定位問題

建議通過以下步驟鞏固知識:

  1. 手動推導示例代碼的名稱查找過程
  2. 編寫包含模板繼承和友元的復雜案例
  3. 使用不同編譯器(如GCC/Clang)觀察錯誤信息差異

遇到具體問題時,可結合nm工具查看符號表,或使用-fdump-class-hierarchy等編譯器選項分析模板實例化結果。

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

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

相關文章

整合vue+Element UI 開發管理系統

1、 安裝 Node.js 和 npm 確保安裝了 Node.js 和 npm。可以通過 Node.js 官網 下載。 2、 創建 Vue 項目 安裝cli npm install -g vue/cli 使用 Vue CLI 創建一個新的 Vue 項目。 vue create admin-system cd admin-system npm run serve 出現這個頁面表示vue創建成功 安…

3. 軸指令(omron 機器自動化控制器)——>MC_Stop

機器自動化控制器——第三章 軸指令 9 MC_Stop變量?輸入變量?輸出變量?輸入輸出變量 功能說明?指令詳情?時序圖?重啟運動指令?多重啟動運動指令?異常 MC_Stop 使軸減速停止。 指令名稱FB/FUN圖形表現ST表現MC_Stop強制停止FBMC_Stop_instance (Axis :《參數》 ,Execu…

C#中修飾符——abstract、virtual

一、多態簡介 在面向對象編程的過程中&#xff0c;多態體現出來的是【一個接口&#xff0c;多個功能】&#xff1b;多態性體現在2個方面&#xff1a; 1、程序運行時&#xff0c;在方法參數、集合或數組等位置&#xff0c;派生類對象可以作為基類的對象處理&#xff1b;這樣該對…

Spring Boot + Spring Integration整合MQTT打造雙向通信客戶端

1. 概述 本文分兩個章節講解MQTT相關的知識&#xff0c;第一部份主要講解MQTT的原理和相關配置&#xff0c;第二個章節主要講和Spring boot的integration相結合代碼的具體實現&#xff0c;如果想快速實現功能&#xff0c;可直接跳過第一章節查看第二章講。 1.1 MQTT搭建 為了…

2025前端面試題記錄

vue項目目錄的執行順序是怎么樣的&#xff1f; 1、package.json   在執行npm run dev時&#xff0c;會在當前目錄尋找package.json文件&#xff0c;此文件包含了項目的名稱版本、項目依賴等相關信息。 2、webpack.config.js(會被vue-cli腳手架隱藏) 3、vue.config.js   對…

專題|Python貝葉斯網絡BN動態推理因果建模:MLE/Bayes、有向無環圖DAG可視化分析呼吸疾病、汽車效能數據2實例合集

原文鏈接&#xff1a;https://tecdat.cn/?p41199 作為數據科學家&#xff0c;我們始終在探索能夠有效處理復雜系統不確定性的建模工具。本專題合集系統性地解構了貝葉斯網絡&#xff08;BN&#xff09;這一概率圖模型在當代數據分析中的創新應用&#xff0c;通過開源工具bnlea…

WX小程序

下載 package com.sky.utils;import com.alibaba.fastjson.JSONObject; import org.apache.http.NameValuePair; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.Cl…

Vulnhub-wordpress通關攻略

姿勢一、后臺修改模板拿WebShell 第一步&#xff1a;進?Vulhub靶場并執?以下命令開啟靶場&#xff1b;在瀏覽器中訪問并安裝好.... 第二步&#xff1a;找到外觀--編輯--404.php&#xff0c;將原內容刪除并修改為一句話木馬&#xff0c;點擊更新--File edited successfully. &…

Spring Boot(十六):攔截器Interceptor

攔截器的簡介 攔截器&#xff08;Interceptor&#xff09;?是Spring框架中的概?念&#xff0c;它同樣適?用于Spring Boot&#xff0c;?因為Spring Boot是基于Spring框架的。攔截器是?一種AOP&#xff08;面向切面編程&#xff09;?的輕量級實現方式&#xff0c;它允許我…

Kotlin v2.1.20 發布,標準庫又有哪些變化?

大家吼哇&#xff01;就在三小時前&#xff0c;Kotlin v2.1.20 發布了&#xff0c;更新的內容也已經在官網上更新&#xff1a;What’s new in Kotlin 2.1.20 。 我粗略地看了一下&#xff0c;下面為大家選出一些我比較感興趣、且你可能也會感興趣的內容。 注意&#xff01;這里…

開源鏈動2+1模式、AI智能名片與S2B2C商城小程序源碼在社交電商渠道拓寬中的協同應用研究

摘要&#xff1a;本文基于"開源鏈動21模式""AI智能名片""S2B2C商城小程序源碼"三大技術要素&#xff0c;探討社交電商時代商家渠道拓寬的創新路徑。通過解析各技術的核心機制與應用場景&#xff0c;結合京東便利店等實際案例&#xff0c;論證其對…

【藍橋杯速成】| 10.回溯切割

前面兩篇內容我們都是在做有關回溯問題的組合應用 今天的題目主題是&#xff1a;回溯法在切割問題的應用 題目一&#xff1a;分割回文串 問題描述 131. 分割回文串 - 力扣&#xff08;LeetCode&#xff09; 給你一個字符串 s&#xff0c;請你將 s 分割成一些 子串&#xff…

【嵌入式硬件】三款DCDC調試筆記

關于開關電源芯片&#xff0c;重點關注輸入電源范圍、輸出電流、最低壓降。 1.MP9943: 以MP9943為例&#xff0c;輸入電壓范圍4-36V&#xff0c;輸出最大電流3A&#xff0c;最低壓降為0.3V 調整FB使正常輸出為5.06V 給定6V空載、5V空載、5V帶2A負載的情況&#xff1a; 6V帶2A…

2025年03月18日柯萊特(外包寧德)一面前端面試

目錄 自我介紹你怎么從0到1搭建項目的webpack 的構建流程手寫webpack插件你有什么想問我的嗎 2. 你怎么從 0 到 1 搭建項目的 在面試中回答從 0 到 1 搭建前端項目&#xff0c;可按以下詳細步驟闡述&#xff1a; 1. 項目前期準備 需求理解與分析 和產品經理、客戶等相關人…

在vitepress中使用vue組建,然后引入到markdown

在 VitePress 中&#xff0c;每個 Markdown 文件都被編譯成 HTML&#xff0c;而且將其作為 Vue 單文件組件處理。這意味著可以在 Markdown 中使用任何 Vue 功能&#xff0c;包括動態模板、使用 Vue 組件或通過添加 <script> 標簽為頁面的 Vue 組件添加邏輯。 值得注意的…

Jupyter Notebook 常用命令(自用)

最近有點忘記了一些常見命令&#xff0c;這里就記錄一下&#xff0c;懶得找了。 文章目錄 一、文件操作命令1. %cd 工作目錄2. %pwd 顯示路徑3. !ls 列出文件4. !cp 復制文件5. !mv 移動或重命名6. !rm 刪除 二、代碼調試1. %time 時間2. %timeit 平均時長3. %debug 調試4. %ru…

Java面試黃金寶典12

1. 什么是 Java 類加載機制 定義 Java 類加載機制是 Java 程序運行時的關鍵環節&#xff0c;其作用是把類的字節碼文件&#xff08;.class 文件&#xff09;加載到 Java 虛擬機&#xff08;JVM&#xff09;中&#xff0c;并且將字節碼文件轉化為 JVM 能夠識別的類對象。整個類…

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

模板實例化 核心知識點解析多選題設計題關鍵點總結 核心知識點解析 兩階段查找&#xff08;Two-Phase Lookup&#xff09; 原理&#xff1a; 模板在編譯時分兩個階段處理&#xff1a; 第一階段&#xff08;定義時&#xff09;&#xff1a;檢查模板語法和非依賴名稱&#xff0…

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 其它特…