第十四章:模板实例化_《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.mzph.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;将数据从输入设备拷贝到内存就叫作…