当前位置: 首页 > article >正文

C++泛型编程指南03-CTAD

文章目录

      • C++17 自定义类型推断指引(CTAD)深度解析
        • 一、基础概念
          • 1. 核心作用
          • 2. 工作原理
        • 二、标准库中的 CTAD 应用
          • 1. 容器类型推导
          • 2. 智能指针推导
          • 3. 元组类型推导
        • 三、自定义推导指引语法
          • 1. 基本语法结构
          • 2. 典型应用场景
        • 四、推导指引设计模式
          • 1. 迭代器范围构造
          • 2. 工厂函数模拟
          • 3. 多参数类型合成
        • 五、编译器行为规则
          • 1. 隐式生成规则
          • 2. 显式指引优先级
        • 六、高级应用技巧
          • 1. 类型萃取结合
          • 2. 可变参数推导
          • 3. 继承体系处理
        • 七、典型问题与解决方案
          • 1. 构造函数重载冲突
          • 2. 部分参数推导
          • 3. 防止错误推导
        • 八、CTAD 最佳实践
        • 九、与其他特性的交互
        • 十、编译器支持与版本控制
  • CTAD例子
      • 示例 1: 自定义容器推导指引
      • 示例 2: 自定义工厂函数推导指引
      • 示例 3: 结合概念约束的推导指引
      • 示例 4: 使用默认模板参数的 CTAD
      • 示例 5: 结合类型转换的 CTAD
      • 示例 6: 使用可变参数模板的 CTAD

C++17 自定义类型推断指引(CTAD)深度解析

CTAD(Class Template Argument Deduction,类模板参数推导)是 C++17 引入的重要特性,允许编译器根据构造函数参数自动推导类模板参数类型。该特性通过 用户自定义推导指引(User-defined Deduction Guides)实现模板参数类型的智能推导。


一、基础概念
1. 核心作用
  • 消除冗余类型声明:无需显式指定模板参数类型
  • 提升代码简洁性:使类模板使用方式接近普通类
  • 增强标准库易用性:支持std::vector{1,2,3}式初始化
2. 工作原理
template<typename T>
struct MyWrapper {MyWrapper(T value);  // 构造函数
};// 使用 CTAD
MyWrapper w(42);  // 推导为 MyWrapper<int>

二、标准库中的 CTAD 应用
1. 容器类型推导
std::vector data{1, 2, 3};       // 推导为 vector<int>
std::list names{"Alice", "Bob"}; // 推导为 list<const char*>
2. 智能指针推导
auto p = std::make_shared(5.0);  // shared_ptr<double>
auto u = std::make_unique("text"); // unique_ptr<const char*>
3. 元组类型推导
std::tuple tpl(42, 3.14, "C++"); // tuple<int, double, const char*>

三、自定义推导指引语法
1. 基本语法结构
template<模板参数列表>
ClassName(构造函数参数类型列表) -> 目标模板实例化类型;
2. 典型应用场景
template<typename T>
struct CustomContainer {CustomContainer(T* ptr, size_t size);  // 指针+大小构造
};// 推导指引:从数组创建容器
template<typename T, size_t N>
CustomContainer(T(&)[N]) -> CustomContainer<T>;

四、推导指引设计模式
1. 迭代器范围构造
template<typename T>
class DataSet {
public:template<typename Iter>DataSet(Iter begin, Iter end);
};// 推导指引
template<typename Iter>
DataSet(Iter, Iter) -> DataSet<typename std::iterator_traits<Iter>::value_type>;
2. 工厂函数模拟
template<typename T>
struct Factory {template<typename... Args>Factory(Args&&... args);
};// 推导指引:根据构造参数推导类型
template<typename... Args>
Factory(Args&&...) -> Factory<std::common_type_t<Args...>>;
3. 多参数类型合成
template<typename T, typename U>
struct Pair {T first;U second;Pair(const T& t, const U& u);
};// 推导指引:自动合成类型
Pair(const auto&, const auto&) -> Pair<std::decay_t<decltype(arg1)>, std::decay_t<decltype(arg2)>>;

五、编译器行为规则
1. 隐式生成规则

当未显式提供推导指引时,编译器会尝试:

  • 匹配所有构造函数
  • 对每个构造函数生成隐式推导指引
template<typename T>
struct Box {Box(T);           // 生成 Box(T) -> Box<T>Box(T, T);        // 生成 Box(T, T) -> Box<T>
};
2. 显式指引优先级
template<typename T>
struct Example {Example(T);Example(int);
};// 显式指引优先于隐式生成
Example(int) -> Example<std::string>;Example e1(42);  // 使用显式指引 → Example<std::string>
Example e2(3.14); // 使用隐式指引 → Example<double>

六、高级应用技巧
1. 类型萃取结合
template<typename T>
struct SmartPointer {template<typename U>SmartPointer(U* ptr);
};// 使用类型萃取约束指针类型
template<typename U>
requires std::is_base_of_v<BaseClass, U>
SmartPointer(U*) -> SmartPointer<BaseClass>;
2. 可变参数推导
template<typename... Ts>
struct TupleWrapper {TupleWrapper(Ts... values);
};// 推导可变参数类型
TupleWrapper(Ts...) -> TupleWrapper<Ts...>;
3. 继承体系处理
template<typename T>
struct Base {};template<typename T>
struct Derived : Base<T> {Derived(T);
};// 处理基类模板参数推导
Derived(T) -> Derived<T>;  // 确保正确推导基类参数

七、典型问题与解决方案
1. 构造函数重载冲突
template<typename T>
struct Conflicting {Conflicting(int);Conflicting(T);
};// 解决方案:显式指定优先级
Conflicting(int) -> Conflicting<int>;
Conflicting(T) -> Conflicting<T>;
2. 部分参数推导
template<typename T, typename U>
struct MixedType {MixedType(T, U);
};// 显式指定部分参数类型
template<typename U>
MixedType(const char*, U) -> MixedType<std::string, U>;
3. 防止错误推导
template<typename T>
struct Dangerous {Dangerous(std::initializer_list<T>);
};// 限制初始化列表类型
template<typename T>
Dangerous(std::initializer_list<T>) -> Dangerous<T>;  // 防止类型退化

八、CTAD 最佳实践
  1. 谨慎设计构造函数:避免过于通用的构造函数签名
  2. 优先使用显式指引:明确表达设计意图
  3. 结合概念约束(C++20):增强类型安全性
  4. 单元测试验证:确保推导结果符合预期
  5. 文档注释说明:记录特殊推导规则

九、与其他特性的交互
特性组合应用场景示例代码片段
CTAD + 结构化绑定同时推导容器元素类型和结构auto [x,y] = Point{1,2};
CTAD + 概念约束类型安全的泛型构造requires integral<T> Vector(T init)
CTAD + 变参模板灵活的类型组合推导Tuple{1, "text", 3.14}
CTAD + 移动语义优化资源管理类型推导UniquePtr ptr(new Resource);

十、编译器支持与版本控制
编译器最低支持版本特性完整度
GCC7.0+完整支持
Clang5.0+完整支持
MSVC2017 15.7+完整支持

通过合理应用 CTAD 技术,开发者可以:

  • 显著提升模板类使用的直观性
  • 减少冗余的类型声明代码
  • 增强模板库的易用性和可维护性
  • 实现更优雅的 API 设计

建议在实际工程中结合编译器的诊断信息和静态断言(static_assert)进行严格验证,确保类型推导结果符合预期。

CTAD例子

当然可以。以下是完整的代码示例,包括了之前提到的改进和新增加的例子。每个例子都展示了类模板参数推导(CTAD)的不同应用方式。

示例 1: 自定义容器推导指引

#include <iostream>
#include <typeinfo>template<typename T>
class Container {
public:Container(T* ptr, size_t size) : data(ptr), size(size) {}void print() const {std::cout << "Container of type: " << typeid(T).name() << std::endl;}private:T* data;size_t size;
};// 用户自定义推导指引
template<typename T, size_t N>
Container(T(&)[N]) -> Container<T>;int main() {int arr[] = {1, 2, 3};Container container(arr);  // 推导为 Container<int>container.print();  // 输出 "Container of type: i" (取决于编译器)
}

示例 2: 自定义工厂函数推导指引

#include <iostream>
#include <typeinfo>
#include <type_traits>template<typename T>
class Widget {
public:Widget(T value) : value(value) {}void print() const {std::cout << "Widget of type: " << typeid(T).name() << std::endl;}private:T value;
};// 用户自定义推导指引
template<typename... Args>
Widget(Args&&...) -> Widget<std::common_type_t<Args...>>;int main() {Widget w(1, 2.0, "three");  // 推导为 Widget<double>w.print();  // 输出 "Widget of type: d" (取决于编译器)
}

示例 3: 结合概念约束的推导指引

#include <concepts>
#include <iostream>
#include <typeinfo>template<typename T>
concept Integral = std::is_integral_v<T>;template<Integral T>
class SafeInteger {
public:SafeInteger(T val) : value(val) {}void print() const {std::cout << "SafeInteger of type: " << typeid(T).name() << std::endl;}private:T value;
};// 用户自定义推导指引
template<Integral T>
SafeInteger(T) -> SafeInteger<T>;int main() {SafeInteger si(42);  // 推导为 SafeInteger<int>si.print();  // 输出 "SafeInteger of type: i" (取决于编译器)
}

示例 4: 使用默认模板参数的 CTAD

#include <iostream>
#include <typeinfo>template<typename T, typename U = int>
class Pair {
public:T first;U second;Pair(T f, U s) : first(f), second(s) {}void print() const {std::cout << "Pair of types: " << typeid(T).name() << ", " << typeid(U).name() << std::endl;}
};// 用户自定义推导指引
template<typename T, typename U>
Pair(T, U) -> Pair<T, U>;int main() {Pair pair(10, 20.5); // 推导为 Pair<int, double>,U 被自动推导为 doublepair.print(); // 输出 "Pair of types: i, d" (取决于编译器)
}

示例 5: 结合类型转换的 CTAD

#include <iostream>
#include <typeinfo>class Base {};
class Derived : public Base {};template<typename T>
class Wrapper {
public:Wrapper(T* ptr) : ptr(ptr) {}void print() const {std::cout << "Wrapper of type: " << typeid(T).name() << std::endl;}private:T* ptr;
};// 用户自定义推导指引
template<typename T>
Wrapper(T*) -> Wrapper<T>;int main() {Derived derived;Wrapper wrapper(&derived); // 自动推导为 Wrapper<Derived>wrapper.print(); // 输出 "Wrapper of type: 7Derived" (取决于编译器)
}

示例 6: 使用可变参数模板的 CTAD

#include <iostream>
#include <typeinfo>template<typename... Args>
class TupleHolder {
public:void print() const {std::cout << "TupleHolder contains types: ";((std::cout << typeid(Args).name() << " "), ...);std::cout << std::endl;}
};// 用户自定义推导指引
template<typename... Args>
TupleHolder(Args...) -> TupleHolder<Args...>;int main() {TupleHolder holder(1, 'a', 3.14); // 推导为 TupleHolder<int, char, double>holder.print(); // 输出 "TupleHolder contains types: i a d" (取决于编译器)
}

这些例子覆盖了从简单的容器推导到复杂的类型转换和可变参数模板的应用。通过使用CTAD技术,您可以简化模板类的实例化过程,并结合其他现代C++特性来增强代码的灵活性和安全性。希望这些示例能帮助您更好地理解和应用CTAD技术。

相关文章:

C++泛型编程指南03-CTAD

文章目录 C17 自定义类型推断指引&#xff08;CTAD&#xff09;深度解析一、基础概念1. 核心作用2. 工作原理 二、标准库中的 CTAD 应用1. 容器类型推导2. 智能指针推导3. 元组类型推导 三、自定义推导指引语法1. 基本语法结构2. 典型应用场景 四、推导指引设计模式1. 迭代器范…...

记8(高级API实现手写数字识别

目录 1、Keras&#xff1a;2、Sequential模型&#xff1a;2.1、建立Sequential模型&#xff1a;modeltf.keras.Sequential()2.2、添加层&#xff1a;model.add(tf.keras.layers.层)2.3、查看摘要&#xff1a;model.summary()2.4、配置训练方法&#xff1a;model.compile(loss,o…...

88.[4]攻防世界 web php_rce

之前做过&#xff0c;回顾&#xff08;看了眼之前的wp,跟没做过一样&#xff09; 属于远程命令执行漏洞 在 PHP 里&#xff0c;system()、exec()、shell_exec()、反引号&#xff08;&#xff09;等都可用于执行系统命令。 直接访问index.php没效果 index.php?sindex/think\a…...

23.Word:小王-制作公司战略规划文档❗【5】

目录 NO1.2.3.4 NO5.6​ NO7.8.9​ NO10.11​ NO12​ NO13.14 NO1.2.3.4 布局→页面设置对话框→纸张&#xff1a;纸张大小&#xff1a;宽度/高度→页边距&#xff1a;上下左右→版式&#xff1a;页眉页脚→文档网格&#xff1a;勾选只指定行网格✔→ 每页&#xff1a;…...

在Arm芯片苹果Mac系统上通过homebrew安装多版本mysql并解决各种报错,感谢deepseek帮助解决部分问题

背景&#xff1a; 1.苹果设备上安装mysql&#xff0c;随着苹果芯片的推出&#xff0c;很多地方都变得不一样了。 2.很多时候为了老项目能运行&#xff0c;我们需要能安装mysql5.7或者mysql8.0或者mysql8.2.虽然本文编写时最新的默认mysql已经是9.2版本。 安装步骤 1.执行hom…...

C++【iostream】数据库的部分函数功能介绍

在 C 编程世界中&#xff0c;iostream 库扮演着举足轻重的角色&#xff0c;它是 C 标准库的核心组成部分&#xff0c;为程序提供了强大的输入输出功能。无论是简单的控制台交互&#xff0c;还是复杂的文件操作&#xff0c;iostream 库都能提供便捷高效的解决方案。本文将深入剖…...

数据结构 树1

目录 前言 一&#xff0c;树的引论 二&#xff0c;二叉树 三&#xff0c;二叉树的详细理解 四&#xff0c;二叉搜索树 五&#xff0c;二分法与二叉搜索树的效率 六&#xff0c;二叉搜索树的实现 七&#xff0c;查找最大值和最小值 指针传递 vs 传引用 为什么指针按值传递不会修…...

【实战篇章】深入探讨:服务器如何响应前端请求及后端如何查看前端提交的数据

文章目录 深入探讨&#xff1a;服务器如何响应前端请求及后端如何查看前端提交的数据一、服务器如何响应前端请求HTTP 请求生命周期全解析1.前端发起 HTTP 请求&#xff08;关键细节强化版&#xff09;2. 服务器接收请求&#xff08;深度优化版&#xff09; 二、后端如何查看前…...

玩转ChatGPT:DeepSeek测评(科研思路梳理)

一、写在前面 DeepSeek-R1出圈了&#xff0c;把OpenAI的o3-mini模型都提前逼上线了&#xff08;还免费使用&#xff09;。 都号称擅长深度推理&#xff0c;那么对于科研牛马的帮助有多大呢&#xff1f; 我连夜试一试。 二、科研思路梳理 有时候我们牛马们做了一堆结果以后&…...

python学opencv|读取图像(五十三)原理探索:使用cv.matchTemplate()函数实现最佳图像匹配

【1】引言 前序学习进程中&#xff0c;已经探索了使用cv.matchTemplate()函数实现最佳图像匹配的技巧&#xff0c;并且成功对两个目标进行了匹配。 相关文章链接为&#xff1a;python学opencv|读取图像&#xff08;五十二&#xff09;使用cv.matchTemplate()函数实现最佳图像…...

AJAX RSS Reader:技术解析与应用场景

AJAX RSS Reader:技术解析与应用场景 引言 随着互联网的快速发展,信息量呈爆炸式增长。为了方便用户快速获取感兴趣的信息,RSS(Really Simple Syndication)技术应运而生。AJAX RSS Reader作为一种基于AJAX技术的信息读取工具,在用户体验和信息获取方面具有显著优势。本…...

Linux环境下的Java项目部署技巧:安装 Mysql

查看 myslq 是否安装&#xff1a; rpm -qa|grep mysql 如果已经安装&#xff0c;可执行命令来删除软件包&#xff1a; rpm -e --nodeps 包名 下载 repo 源&#xff1a; http://dev.mysql.com/get/mysql80-community-release-el7-7.noarch.rpm 执行命令安装 rpm 源(根据下载的…...

gitea - fatal: Authentication failed

文章目录 gitea - fatal: Authentication failed概述run_gitea_on_my_pkm.bat 笔记删除windows凭证管理器中对应的url认证凭证启动gitea服务端的命令行正常用 TortoiseGit 提交代码备注END gitea - fatal: Authentication failed 概述 本地的git归档服务端使用gitea. 原来的用…...

计算机网络安全与运维的关键 —— 常用端口全解析

目录 前言 常见端口分类及用途 20 端口&#xff08;FTP 数据传输&#xff09; 21 端口&#xff08;FTP 消息控制&#xff09; 22 端口&#xff08;SSH&#xff09; 23 端口&#xff08;Telnet&#xff09; 25 端口&#xff08;SMTP&#xff09; 53 端口&#xff08;DNS&…...

JavaScript Navigator:深入理解浏览器导航机制

JavaScript Navigator:深入理解浏览器导航机制 引言 在Web开发中,浏览器导航是用户与网页交互的重要部分。JavaScript Navigator对象提供了丰富的API,允许开发者深入理解并控制浏览器的导航行为。本文将详细介绍JavaScript Navigator对象的功能、使用方法以及在实际开发中…...

笔灵ai写作技术浅析(三):深度学习

笔灵AI写作的深度学习技术主要基于Transformer架构,尤其是GPT(Generative Pre-trained Transformer)系列模型。 1. Transformer架构 Transformer架构由Vaswani等人在2017年提出,是GPT系列模型的基础。它摒弃了传统的循环神经网络(RNN)和卷积神经网络(CNN),完全依赖自…...

Linux-CentOS的yum源

1、什么是yum yum是CentOS的软件仓库管理工具。 2、yum的仓库 2.1、yum的远程仓库源 2.1.1、国内仓库 国内较知名的网络源(aliyun源&#xff0c;163源&#xff0c;sohu源&#xff0c;知名大学开源镜像等) 阿里源:https://opsx.alibaba.com/mirror 网易源:http://mirrors.1…...

< OS 有关> BaiduPCS-Go 程序的 菜单脚本 Script: BaiduPCS-Go.Menu.sh (bdgo.sh)

目标&#xff1a; 使用 日本阿里云的 VPM 传输文件。 暂时方案&#xff1a; 使用 主机JPN 下载 https://huggingface.co/ 上模型从 JPN 放到 度狗上在家里从狗度下载 为了减少编程&#xff0c;尽量使用现在软件 &#xff0c;就找到 GitHub - qjfoidnh/BaiduPCS-Go: iikira…...

【前端学习路线】前端优化 详细知识点学习路径(附学习资源)

&#x1f4da;学习资源&#xff1a; 前端开发&#xff1a;零基础入门到项目实战 >> 前端开发&#xff1a;边学边练 >> 原学习路径下载 >>...

deepseek v3 搭建个人知识库

目录 deepseek-r1本地部署&#xff0c;这个比较好&#xff0c;推荐 Chatbox连接ollama服务 知乎教程&#xff0c;需要注册&#xff1a; deepseek-r1本地部署&#xff0c;这个比较好&#xff0c;推荐 公司数据不泄露&#xff0c;DeepSeek R1本地化部署web端访问个人知识库搭建…...

LeetCode 2909. 元素和最小的山形三元组 II

**### LeetCode 2909. 元素和最小的山形三元组 II 问题描述 给定一个下标从 0 开始的整数数组 nums&#xff0c;我们需要找到一个“山形三元组”&#xff08;i, j, k&#xff09;满足以下条件&#xff1a; i < j < knums[i] < nums[j] 且 nums[k] < nums[j] 并…...

C#面试常考随笔4:int? 和 int的区别,以及int?的运用场景?

可空性 int?&#xff1a;它是int的可空类型&#xff0c;允许将null赋值给该变量。int?实际上是Nullable<int>的缩写形式&#xff0c;是一个可以为null的整数类型。例如&#xff1a;int? num2 null;或者int? num3 10;都是合法的。 内存分配与存储 int?&#xff…...

【零拷贝】

目录 一&#xff1a;了解IO基础概念 二&#xff1a;数据流动的层次结构 三&#xff1a;零拷贝 1.传统IO文件读写 2.mmap 零拷贝技术 3.sendFile 零拷贝技术 一&#xff1a;了解IO基础概念 理解CPU拷贝和DMA拷贝 ​ 我们知道&#xff0c;操作系统对于内存空间&…...

【Elasticsearch】_all 查询

在 Elasticsearch 中&#xff0c;_all 查询是一种特殊的查询方式&#xff0c;用于在多个索引或数据流中执行搜索操作&#xff0c;而无需显式指定每个目标索引或数据流的名称。以下是关于 _all 查询的详细说明&#xff1a; _all 查询概述 用途&#xff1a;_all 查询允许您在多个…...

扩散模型(一)

在生成领域&#xff0c;迄今为止有几个主流的模型&#xff0c;分别是 GAN, VAE&#xff0c;Flow 以及 Diffusion 模型。 GAN&#xff1a;GAN 的学习机制是对抗性学习&#xff0c;通过生成器和判别器的对抗博弈来进行学习&#xff0c;这种竞争机制促使生成器不断提升生成能力&a…...

【LLM-agent】(task6)构建教程编写智能体

note 构建教程编写智能体 文章目录 note一、功能需求二、相关代码&#xff08;1&#xff09;定义生成教程的目录 Action 类&#xff08;2&#xff09;定义生成教程内容的 Action 类&#xff08;3&#xff09;定义教程编写智能体&#xff08;4&#xff09;交互式操作调用教程编…...

【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】2.12 连续数组:为什么contiguous这么重要?

2.12 连续数组&#xff1a;为什么contiguous这么重要&#xff1f; 目录 #mermaid-svg-wxhozKbHdFIldAkj {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-wxhozKbHdFIldAkj .error-icon{fill:#552222;}#mermaid-svg-…...

O3 模型正式上线,能否与 DeepSeek 一较高下?

OpenAI 最近推出了 GPT O3 模型&#xff0c;并对 ChatGPT Plus 用户的 O3-mini 版本进行了升级&#xff0c;提升了每日消息限额&#xff0c;从 50 条增加至 150 条。这一调整大大提升了用户体验&#xff0c;让更多用户有机会深入体验 O3 模型的能力。那么&#xff0c;O3 模型的…...

在C++中,成员变量必须在对象构造完成前初始化,但初始化的方式有多种...

在C中&#xff0c;成员变量必须在对象构造完成前初始化&#xff0c;但初始化的方式可以有多种&#xff0c;具体取决于成员变量的类型和设计需求。以下是C中成员变量初始化的规则和相关机制&#xff1a; 1. 成员变量必须初始化 如果成员变量是基本类型&#xff08;如 int、doub…...

计算机网络 应用层 笔记1(C/S模型,P2P模型,FTP协议)

应用层概述&#xff1a; 功能&#xff1a; 常见协议 应用层与其他层的关系 网络应用模型 C/S模型&#xff1a; 优点 缺点 P2P模型&#xff1a; 优点 缺点 DNS系统&#xff1a; 基本功能 系统架构 域名空间&#xff1a; DNS 服务器 根服务器&#xff1a; 顶级域…...