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

C++ 萃取技术——值萃取

目录

  • C++ 萃取技术——值萃取
    • 1. 常规范例
    • 2. 判断是否为 void 类型的范例
    • 3. 判断两个类型是否相等

C++ 萃取技术——值萃取

在 C++ 编程中,萃取技术(Traits)是模板元编程的一个核心概念,用于在编译时提供类型相关的信息。其中,值萃取(Value Traits)是一种特定形式的萃取技术,它允许程序员为不同的数据类型定义特定的属性或值。

值萃取本质上是一个模板结构体或者模板类,它为给定的类型提供编译时可计算的值或类型。其中包含一个静态常量表达式(通常是static_constexpr成员),这个常量表达式的值基于模板参数进行计算。值萃取可以用来确定某些类型属性(如是否是整型、大小等),或者根据类型计算某些特定的值(如阶乘、斐波那契数等)。

1. 常规范例

首先定义一个简单的类 A,包含一个整数成员变量,并重载 += 操作符以支持累加操作。

#include <iostream>class A {
public:A(int v1, int v2) : m_i(v1 + v2) {}int m_i;A& operator +=(const A& obj){m_i += obj.m_i;return *this;};
};

随后,定义模板结构 SumFixedTraitraits 用于固定萃取。这个结构为不同的数据类型定义了适当的求和类型和初始值。

template<typename T>
struct SumFixedTraitraits; // 基础模板声明,无需实现// 特化版本为 char 类型
template<>
struct SumFixedTraitraits<char>
{using sumT = int;static sumT initValue() {return 0;}
};// 特化版本为 int 类型
template<>
struct SumFixedTraitraits<int>
{using sumT = __int64;static sumT initValue(){return 0;}
};// 特化版本为 double 类型
template<>
struct SumFixedTraitraits<double> 
{using sumT = double;static sumT initValue() {return 0.0;}
};// 特化版本为类 A 类型
template<>
struct SumFixedTraitraits<A> 
{using sumT = A;static sumT initValue() {return sumT{0, 0};}
};

一个泛型函数 funcsum 利用 SumFixedTraitraits 来确定累加器的类型和初始值,并计算从 beginend 的数组元素之和。

template<typename T>
auto funcsum(const T* begin, const T* end)
{// 传入一个类型(T),返回一个类型(sumT),这是固定萃取的运用using sumT = typename SumFixedTraitraits<T>::sumT;sumT sum = SumFixedTraitraits<T>::initValue();for(;;){sum += (*begin);if(begin == end){break;}++begin;}return sum;
}

main 函数中,对各种类型的数组使用 funcsum 并输出结果。

int main() 
{int myintarray1[] = {10, 15, 20};int myintarray2[] = {1000000000, 1500000000, 2000000000};char mychararray[] = "abc";double mydblarray1[] = {12.8, 15.8, 20.6};A myaobjarray1[] = {A{2, 3}, A{6, 8}, A{11, 12}};std::cout << funcsum(myintarray1, myintarray1 + 3) << std::endl;std::cout << funcsum(myintarray2, myintarray2 + 3) << std::endl;std::cout << static_cast<int>(funcsum(mychararray, mychararray + 3)) << std::endl;std::cout << funcsum(mydblarray1, mydblarray1 + 3) << std::endl;std::cout << funcsum(myaobjarray1, myaobjarray1 + 3).m_i << std::endl;return 0;
}

在上述示例中,值萃取技术的应用非常显著。通过定义 SumFixedTraitraits 模板结构,可以为不同的数据类型特定化适合的求和类型和初始值,展示了如何利用模板特化来适应不同数据类型的需求。

解析常规范例中的值萃取应用

  1. 类型特化
    • SumFixedTraitraits 结构中,对于每个数据类型(如 char, int, double, A 类),定义了一个特化版本。这些特化版本分别定义了一个 sumT 类型和一个 initValue 静态方法。sumT 确定了用于累计求和的数据类型,而 initValue 提供了该数据类型的初始值。
  2. 类型安全与初值设定
    • 通过为每个类型提供一个明确的初始值,例如对于 char 类型是 0(整数),对于 double0.0,这样的设计确保了在进行求和操作时,累加器的初始状态是正确的,且符合数据类型的性质。这也避免了任何隐式类型转换,增加了代码的类型安全性。
  3. 泛型函数的使用
    • funcsum 函数展示了如何使用值萃取。它首先通过 typename SumFixedTraitraits<T>::sumT 获取适当的累加器类型。然后使用 SumFixedTraitraits<T>::initValue() 获取累加器的初始值。这种方式使得 funcsum 函数能够适用于任何定义了相应特化的数据类型。
  4. 扩展性和灵活性
    • 如果需要支持新的数据类型,只需为该类型添加 SumFixedTraitraits 的特化版本即可。这种设计使得原有代码易于扩展且不需要修改,只需增加新的特化定义。

2. 判断是否为 void 类型的范例

C++标准库中有一个类模板is_void,用来判断某个类型是不是void类型。在main()主函数中简单写一段测试代码:

int main()
{std::cout << "int是void类型吗?" << std::is_void<int>::value << std::endl;   // 0std::cout << "void是void类型吗?" << std::is_void<void>::value << std::endl; // 1return 0;
}

那么,这个is_void是怎么实现的呢?其实,is_void就可以看作一个值萃取类模板,这里也写一个值萃取类模板实现is_void类似的功能。

请不要忘记,值萃取类模板的功能是“传入一种类型,萃取出一个值”。

下面范例中,展示了如何使用值萃取类模板来判断一个类型是否为 void 类型。这是 is_void 的简化实现,用以说明值萃取技术在类型判断中的应用。

#include <iostream>template <typename T>
struct voidValueTraits
{static const bool value = false;
};template<> //特化版本
struct voidValueTraits<void>
{static const bool value = true;
};int main()
{std::cout << "int是void类型吗?" << voidValueTraits<int>::value << std::endl;   // 0std::cout << "void是void类型吗?" << voidValueTraits<void>::value << std::endl; // 1return 0;
}

使用场景和优点

  • 类型判断:通过这种方法,可以在编译时判断任何类型是否为 void,这对于一些模板函数或类在处理特定类型时非常有用,比如在模板元编程中做条件编译或者在运行时避免某些行为。
  • 编译时计算:所有的检查都在编译时完成,没有运行时成本。
  • 易于扩展:虽然这里只针对 void 类型进行特化,但这种模式可以被用来检测和萃取任何类型的属性,例如检测是否是指针类型、是否是整数类型等。

3. 判断两个类型是否相等

在 C++ 的标准库中,std::is_same 类模板用于判断两个类型是否相同。类似地,可以利用值萃取技术编写一个自定义的萃取类模板来实现这一功能。以下是如何通过一个简单的值萃取类模板达到与 std::is_same 相似的效果。

首先,创建一个基本模板和一个特化模板:

#include <iostream>//泛化版本
template<typename T1, typename T2>
struct IsSameType
{static const bool value = false;
};
//特化版本
template<typename T1>
struct IsSameType<T1,T1>
{static const bool value = true;
};int main()
{std::cout << IsSameType<int, const int>::value << std::endl; // 0std::cout << IsSameType<int, int>::value << std::endl;       // 1return 0;
}

可以用一个变量模板(不能用别名模板,因为结果是一个值,不是一个类型)简化书写,代码如下。

template<typename T1,typename T2>
const bool IsSame_v = IsSameType<T1,T2>::value;int main()
{std::cout << IsSame_v<int, const int> << std::endl; // 0std::cout << IsSame_v<int, int> << std::endl;        // 1return 0;
}

通过继承自 std::true_typestd::false_type,可以进一步简化模板的定义。这两个类型已经内置了一个 value 静态成员,分别代表布尔值 truefalse

#include <iostream>template<typename T1, typename T2>
struct IsSameType : std::false_type {};
template<typename T1>
struct IsSameType<T1, T1> : std::true_type {};template<typename T1,typename T2>
const bool IsSame_v = IsSameType<T1,T2>::value;int main()
{std::cout << IsSame_v<int, const int> << std::endl; // 0std::cout << IsSame_v<int, int> << std::endl;        // 1return 0;
}

从上面的代码中可以看到,类模板IsSameType是空的,不需要有任何代码行,只需要继承std::false_typestd::true_type表达一个“假”或“真”的含义就行了,std::false_typestd::true_type类模板中本身就定义了value静态成员。

解析 IsSameType 的实现

  1. 基本及特化模板定义
    • 泛化版本的 IsSameType<T1, T2> 默认将 value 设置为 false,表示两个类型不相同。
    • 特化版本的 IsSameType<T1, T1>value 设置为 true,只有当两个模板参数相同(即 T1T1),才会触发这个特化版本,表示两个类型相同。
  2. 变量模板简化
    • IsSame_v<T1, T2> 是一个变量模板,它直接提供了一种更简洁的方式来获取两个类型是否相同的结果。这种方式在代码中更加直观和易于使用。
  3. 使用 std::true_typestd::false_type
    • 优化后的 IsSameType 直接继承自 std::false_typestd::true_type。这些类型已经在标准库中定义了 value 成员,分别为 falsetrue。通过继承这些类型,可以省略在自己的结构体中定义 value 的代码,并且能够直接利用标准库提供的功能。
    • 这种继承方式还允许 IsSameType 与其他标准类型萃取类无缝集成,因为它们都遵循相同的模式。

使用场景和优点

  • 通用性:这种类型比较机制可以在很多场合使用,例如在编写模板代码时进行特化或者在编译时进行类型检查。
  • 编译时优化:所有的类型检查都是在编译时完成的,没有运行时开销。
  • 易于整合和扩展:使用标准的 true_typefalse_type 使得这个萃取技术与其他标凑库组件高度兼容,易于整合和扩展。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 机器学习(Machine Learning, ML)和深度学习(Deep Learning, DL)对比
  • c4d的重命名工具(支持模型和材质) 及 python窗口定义
  • 第四天旅游线路预览——从贾登峪到喀纳斯景区入口(贾登峪游客服务中心)
  • [数据集][目标检测]智慧交通铁路异物入侵检测数据集VOC+YOLO格式802张7类别
  • [网络][CISCO]Cisco-PIX配置详解
  • 创建Django 项目
  • 【python计算机视觉编程——10.OpenCV】
  • 图新地球-将地图上大量的地标点批量输出坐标到csv文件【kml转excel】
  • Linux驱动开发-字符设备驱动开发
  • 如何在本地计算机中打开远程服务器的Jupyter notebook
  • leetcode hot100刷题【持续更新】
  • 深度挖掘| 如何高效实现Cloudera 安装之基础环境搭建
  • 学习贵在善假于物
  • LCR 024
  • 【android10】【binder】【2.servicemanager启动——全源码分析】
  • 《微软的软件测试之道》成书始末、出版宣告、补充致谢名单及相关信息
  • MD5加密原理解析及OC版原理实现
  • rabbitmq延迟消息示例
  • RedisSerializer之JdkSerializationRedisSerializer分析
  • spring + angular 实现导出excel
  • Xmanager 远程桌面 CentOS 7
  • Zsh 开发指南(第十四篇 文件读写)
  • 阿里研究院入选中国企业智库系统影响力榜
  • 道格拉斯-普克 抽稀算法 附javascript实现
  • 规范化安全开发 KOA 手脚架
  • 猫头鹰的深夜翻译:Java 2D Graphics, 简单的仿射变换
  • 每个JavaScript开发人员应阅读的书【1】 - JavaScript: The Good Parts
  • 网页视频流m3u8/ts视频下载
  • 小程序开发中的那些坑
  • 译米田引理
  • 怎么把视频里的音乐提取出来
  • 智能网联汽车信息安全
  • Java性能优化之JVM GC(垃圾回收机制)
  • ​​​​​​​GitLab 之 GitLab-Runner 安装,配置与问题汇总
  • ​MySQL主从复制一致性检测
  • #在线报价接单​再坚持一下 明天是真的周六.出现货 实单来谈
  • (+4)2.2UML建模图
  • (02)Hive SQL编译成MapReduce任务的过程
  • (4)通过调用hadoop的java api实现本地文件上传到hadoop文件系统上
  • (动手学习深度学习)第13章 计算机视觉---图像增广与微调
  • (非本人原创)我们工作到底是为了什么?​——HP大中华区总裁孙振耀退休感言(r4笔记第60天)...
  • (附源码)springboot 房产中介系统 毕业设计 312341
  • (附源码)springboot家庭装修管理系统 毕业设计 613205
  • (附源码)springboot掌上博客系统 毕业设计063131
  • (含笔试题)深度解析数据在内存中的存储
  • (六)Flink 窗口计算
  • (学习日记)2024.03.12:UCOSIII第十四节:时基列表
  • .jks文件(JAVA KeyStore)
  • .mat 文件的加载与创建 矩阵变图像? ∈ Matlab 使用笔记
  • .NET Core Web APi类库如何内嵌运行?
  • .net SqlSugarHelper
  • .Net 路由处理厉害了
  • /bin/rm: 参数列表过长"的解决办法
  • @RestControllerAdvice异常统一处理类失效原因
  • [ Linux ] Linux信号概述 信号的产生