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技术。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/67786.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

deepseek+vscode自动化测试脚本生成

近几日Deepseek大火,我这里也尝试了一下,确实很强。而目前vscode的AI toolkit插件也已经集成了deepseek R1,这里就介绍下在vscode中利用deepseek帮助我们完成自动化测试脚本的实践分享 安装AI ToolKit并启用Deepseek 微软官方提供了一个针对AI辅助的插件,也就是 AI Toolk…

电介质超表面中指定涡旋的非线性生成

涡旋光束在众多领域具有重要应用&#xff0c;但传统光学器件产生涡旋光束的方式限制了其在集成系统中的应用。超表面的出现为涡旋光束的产生带来了新的可能性&#xff0c;尤其是在非线性领域&#xff0c;尽管近些年来已经有一些研究&#xff0c;但仍存在诸多问题&#xff0c;如…

基于Springboot+mybatis+mysql+html图书管理系统2

基于Springbootmybatismysqlhtml图书管理系统2 一、系统介绍二、功能展示1.用户登陆2.用户主页3.图书查询4.还书5.个人信息修改6.图书管理&#xff08;管理员&#xff09;7.学生管理&#xff08;管理员&#xff09;8.废除记录&#xff08;管理员&#xff09; 三、数据库四、其它…

本地部署DeepSeek方法

本地部署完成后的效果如下图&#xff0c;整体与chatgpt类似&#xff0c;只是模型在本地推理。 我们在本地部署主要使用两个工具&#xff1a; ollamaopen-webui ollama是在本地管理和运行大模型的工具&#xff0c;可以直接在terminal里和大模型对话。open-webui是提供一个类…

游戏引擎 Unity - Unity 启动(下载 Unity Editor、生成 Unity Personal Edition 许可证)

Unity Unity 首次发布于 2005 年&#xff0c;属于 Unity Technologies Unity 使用的开发技术有&#xff1a;C# Unity 的适用平台&#xff1a;PC、主机、移动设备、VR / AR、Web 等 Unity 的适用领域&#xff1a;开发中等画质中小型项目 Unity 适合初学者或需要快速上手的开…

【开源免费】基于Vue和SpringBoot的公寓报修管理系统(附论文)

本文项目编号 T 186 &#xff0c;文末自助获取源码 \color{red}{T186&#xff0c;文末自助获取源码} T186&#xff0c;文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

《苍穹外卖》项目学习记录-Day11订单统计

根据起始时间和结束时间&#xff0c;先把begin放入集合中用while循环当begin不等于end的时候&#xff0c;让begin加一天&#xff0c;这样就把这个区间内的时间放到List集合。 查询每天的订单总数也就是查询的时间段是大于当天的开始时间&#xff08;0点0分0秒&#xff09;小于…

【python】python油田数据分析与可视化(源码+数据集)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;专__注&#x1f448;&#xff1a;专注主流机器人、人工智能等相关领域的开发、测试技术。 【python】python油田数据分析与可视化&#xff08…

FBX SDK的使用:基础知识

Windows环境配置 FBX SDK安装后&#xff0c;目录下有三个文件夹&#xff1a; include 头文件lib 编译的二进制库&#xff0c;根据你项目的配置去包含相应的库samples 官方使用案列 动态链接 libfbxsdk.dll, libfbxsdk.lib是动态库&#xff0c;需要在配置属性->C/C->预…

一文讲解HashMap线程安全相关问题(上)

HashMap不是线程安全的&#xff0c;主要有以下几个问题&#xff1a; ①、多线程下扩容会死循环。JDK1.7 中的 HashMap 使用的是头插法插入元素&#xff0c;在多线程的环境下&#xff0c;扩容的时候就有可能导致出现环形链表&#xff0c;造成死循环。 JDK 8 时已经修复了这个问…

python学习——常用的内置函数汇总

文章目录 类型转换函数数学函数常用的迭代器操作函数常用的其他内置函数 类型转换函数 数学函数 常用的迭代器操作函数 实操&#xff1a; from cv2.gapi import descr_oflst [55, 42, 37, 2, 66, 23, 18, 99]# (1) 排序操作 asc_lst sorted(lst) # 升序 desc_lst sorted(l…

MySQL数据库环境搭建

下载MySQL 官网&#xff1a;https://downloads.mysql.com/archives/installer/ 下载社区版就行了。 安装流程 看b站大佬的视频吧&#xff1a;https://www.bilibili.com/video/BV12q4y1477i/?spm_id_from333.337.search-card.all.click&vd_source37dfd298d2133f3e1f3e3c…

如何用微信小程序写春联

​ 生活没有模板,只需心灯一盏。 如果笑能让你释然,那就开怀一笑;如果哭能让你减压,那就让泪水流下来。如果沉默是金,那就不用解释;如果放下能更好地前行,就别再扛着。 一、引入 Vant UI 1、通过 npm 安装 npm i @vant/weapp -S --production​​ 2、修改 app.json …

[SAP ABAP] 静态断点的使用

在 ABAP 编程环境中&#xff0c;静态断点通过关键字BREAK-POINT实现&#xff0c;当程序执行到这一语句时&#xff0c;会触发调试器中断程序的运行&#xff0c;允许开发人员检查当前状态并逐步跟踪后续代码逻辑 通常情况下&#xff0c;在代码的关键位置插入静态断点可以帮助开发…

96,【4】 buuctf web [BJDCTF2020]EzPHP

进入靶场 查看源代码 GFXEIM3YFZYGQ4A 一看就是编码后的 1nD3x.php 访问 得到源代码 <?php // 高亮显示当前 PHP 文件的源代码&#xff0c;用于调试或展示代码结构 highlight_file(__FILE__); // 关闭所有 PHP 错误报告&#xff0c;防止错误信息泄露可能的安全漏洞 erro…

基于深度学习的输电线路缺陷检测算法研究(论文+源码)

输电线路关键部件的缺陷检测对于电网安全运行至关重要&#xff0c;传统方法存在效率低、准确性不高等问题。本研究探讨了利用深度学习技术进行输电线路关键组件的缺陷检测&#xff0c;目的是提升检测的效率与准确度。选用了YOLOv8模型作为基础&#xff0c;并通过加入CA注意力机…

3、从langchain到rag

文章目录 本文介绍向量和向量数据库向量向量数据库 索引开始动手实现rag加载文档数据并建立索引将向量存放到向量数据库中检索生成构成一条链 本文介绍 从本节开始&#xff0c;有了上一节的langchain基础学习&#xff0c;接下来使用langchain实现一个rag应用&#xff0c;并稍微…

DeepSeek-R1大模型本地化部署

前言 Ollama作为一个轻量级、易上手的工具&#xff0c;可以帮助你在自己的电脑上快速部署和运行大型语言模型&#xff0c;无需依赖云端服务。通过加载各种开源模型&#xff0c;比如LLaMA、GPT-J等&#xff0c;并通过简单的命令行操作进行模型推理和测试。 此小结主要介绍使用…

【高级篇 / IPv6】(7.6) ❀ 03. 宽带IPv6 - ADSL拨号宽带上网配置 ❀ FortiGate 防火墙

【简介】大部分ADSL拨号宽带都支持IPv6&#xff0c;这里以ADSL拨号宽带为例&#xff0c;演示在FortiGate防火墙上的配置方法。 准备工作 同上篇文章一样&#xff0c;为了兼顾不熟悉FortiGate防火墙的朋友&#xff0c;我们从基础操作进行演示&#xff0c;熟练的朋友可以跳过这一…

Linux第104步_基于AP3216C之I2C实验

Linux之I2C实验是在AP3216C的基础上实现的&#xff0c;进一步熟悉修改设备树和编译设备树&#xff0c;以及学习如何编写I2C驱动和APP测试程序。 1、AP3216C的原理图 AP3216C集成了一个光强传感器ALS&#xff0c;一个接近传感器PS和一个红外LED&#xff0c;为三合一的环境传感…