C++ — 类型萃取

2023-10-26

类型萃取

在编程中我们可能时常会听到类型萃取这个高大上的"学术名词",我们今天来探究一下这个高大上的学术名词
到底是何方神圣,先看看官方的解释类型萃取使用模板技术来萃取类型(包含自定义类型和内置类型)的某些特
性,用以判断该类型是否含有某些特性,从而在泛型算法中来对该类型进行特殊的处理用来提高效率或者其他。
例如:在STL中的destory算法根据函数的参数类型的特性:是否有trivial destructor来选择对应的策略来进行
destory,如果为内置类型,则不调用该类型的destructor,否则对迭代器范围内的对象调用destructor来进行
destory。
类型萃取的意思其实一点都不难,就是兵来将挡,水来土掩的意思,也就是我根据你传递过来的类型,我再决定
调用那种方法.举个例子吧,当你在使用STL的顺序表的时,当类型为内置类型的时候系统程序使用realloc开辟
空间很有效率,当时当类型为自定义类型时,用realloc显然不可以的,这个时候又用new的方式来。
首先我们应该明白,类型萃取原理是什么??它是依据什么来实现的??


特化==>

类型萃取依靠的就是 模板的特化  
模板特化分为两种: 全特化和偏特化
模板为什么要特化?
因为编译器认为,对于特定的类型,如果你能对某一功能更好的实现,那么就该听你的。
今天我简单认识一下特化怎么使用就好,这个我会有一个博客专门写:
现在我们介绍一下全特化: 
看下面的例子:
template<class T>
class A
{
public:
	A()
	:a(0)
	{}
	~A()
	{
		cout << "这里走了原始的模板"<< endl;
	}
protected:
	T a;
};

template<>
class A<int>
{
public:
	A()
	:a(0)
	{}
	~A()
	{
		cout << "这里走了全特化后int的版本" << endl;
	}
protected:
	int a;
};

void test2()
{
	A<char> a;
	A<int> b;
}

我们先看看程序运行的结果:
我们发现按照我们的意思解释这个就很通顺,a先实例化然后它后析构,b发生了全特化,所以它调用的
是int的模板版本。
下面是我调试时实例化b的时候,发生的事情,程序走的是int类型特化版本,有图有真相:
今天用的全特化就够用了,我们重点是理解类型萃取,好了既然我们了解到类型萃取的原理,现在我们看类型
萃取怎么使用的!

应用实例:

今天我就从顺序表SeqList这里入手:
注意这个顺序表是我们自己写的一个模板类,他现在有一个问题我们需要的是一个动态存储的顺序表,所以
这里会有一个扩容的情况,但是因为模板里面你接收到的类型千奇百怪,所以不可能你只有一种扩容方法,一
个是效率低,其次有的类型你用这种方法又不行,这时候就是类型萃取大展身手的时候了。在顺序表中,当你
使用内置类型时使用realloc扩容空间能好点,当你是自定义类型时你就得老老实实的使用别的方法了。
我先把扩容的那个函数拿出来:
void _CheckCapacity()
	{
		if (_size >= _capacity)
		{
			if (TypeTraits <T>::__IsPODType().Get())
			{
				cout << "这里是内置类型扩容的情况" << endl;
				_a = (T*)realloc(_a, (_capacity * 2 + 3)*sizeof(T));
				_capacity = _capacity * 2 + 3;
			}
			else
			{
				if (_a == NULL)
				{
					_a = new T[3];
				}
				cout << "这里是非内置类型扩容的情况" << endl;
				_capacity = _capacity * 2 + 3;
				T* tmp  = new T[_capacity];
				if (_a)
				{
					for (size_t i = 0; i < _size; ++i)
					{
						tmp[i] = _a[i];
					}
					delete[] _a;
					_a = tmp;
				}
			}
		}
	}


看到这里大家可能都会发现这里 if (TypeTraits <T>::__IsPODType().Get()) 这句其实是最关键的,用这句来区别调用
的是哪个方法。   但是这句里面的 __IsPODType()和Get()是哪里来的呢?
struct __TrueType
{
	bool Get()
	{
		return true;
	}
};

struct __FalseType
{
	bool Get()
	{
		return false;
	}
};
这个是TypeTraits的模板本体,默认__IsPODType为_FalseType.当它为内置类型的特化时  __IsPODType为_TureType
template <class _Tp>
struct TypeTraits
{
	typedef __FalseType   __IsPODType;
};
//从这开始都是特化版本(类型萃取的开始)
template <>
struct TypeTraits< char>
{
	typedef __TrueType     __IsPODType;
};

template <>
struct TypeTraits< unsigned char >
{
	typedef __TrueType     __IsPODType;
};
为了类型萃取,我们把所有的内置类型进行特化, 当然这里我只是把内置类型特化的前两个拿了出来,当T为内置
类型 _IsPODType为_TureType,所以 if (TypeTraits <T>::__IsPODType().Get())表达为真,走的是realloc的方
法。下面看看这些代码的调用理解图:
默认的_TureType为 _FalseType,所以当T为非内置类型时就走的是另外的方法,这就是类型萃取思想,有木有觉得
突然不够高大上了呢。。  虽然它可能不是多么的难,但是我们不能够否认它的实用性和可操作性。
最关键的地方既然我们已经理解了,那么我把代码贴出来,大家根据代码再自己理解一下:

//***********************************************************************************************************//
                                                类型萃取                                                   
//***********************************************************************************************************//
struct __TrueType
{
	bool Get()
	{
		return true;
	}
};

struct __FalseType
{
	bool Get()
	{
		return false;
	}
};

template <class _Tp>
struct TypeTraits
{
	typedef __FalseType   __IsPODType;
};

template <>
struct TypeTraits< char>
{
	typedef __TrueType     __IsPODType;
};

template <>
struct TypeTraits< unsigned char >
{
	typedef __TrueType     __IsPODType;
};

template <>
struct TypeTraits< bool>
{
	typedef __TrueType     __IsPODType;
};

template <>
struct TypeTraits< short>
{
	typedef __TrueType     __IsPODType;
};

template <>
struct TypeTraits< unsigned short >
{
	typedef __TrueType     __IsPODType;
};

template <>
struct TypeTraits< int>
{
	typedef __TrueType     __IsPODType;
};

template <>
struct TypeTraits< unsigned int >
{
	typedef __TrueType     __IsPODType;
};

template <>
struct TypeTraits< long>
{
	typedef __TrueType     __IsPODType;
};

template <>
struct TypeTraits< unsigned long >
{
	typedef __TrueType     __IsPODType;
};

template <>
struct TypeTraits< long long >
{
	typedef __TrueType     __IsPODType;
};

template <>
struct TypeTraits< unsigned long long>
{
	typedef __TrueType     __IsPODType;
};

template <>
struct TypeTraits< float>
{
	typedef __TrueType     __IsPODType;
};

template <>
struct TypeTraits< double>
{
	typedef __TrueType     __IsPODType;
};

template <>
struct TypeTraits< long double >
{
	typedef __TrueType     __IsPODType;
};

template <class _Tp>
struct TypeTraits< _Tp*>
{
	typedef __TrueType     __IsPODType;
};

typedef int DataType;
template <class T>
class SeqList
{
public:
	SeqList()
		:_a(NULL)
		, _capacity(0)
		, _size(0)
	{

	}

	SeqList(const SeqList<T>& s)
	{
		_a = new T[s._size];
		memcpy(_a, s._a, sizeof(T)*s._size);
		_size = s._size;
		_capacity = s._size;
	}

	SeqList<T>& operator= (SeqList<T> s)
	{
		swap(_size, s._size);
		swap(_capacity, s._capacity);
		swap(_a, s._a);
		return *this;
	}

	~SeqList()
	{
		if (TypeTraits <T>::__IsPODType().Get())
		{
			free(_a);
			_capacity = _size = 0;
		}
		else
		{
			delete[] _a;
			_capacity = _size = 0;
		}
	}

	void PushBack(const T& x)
	{
		_CheckCapacity();

		_a[_size] = x;
		++_size;
	}

	void PopBack()
	{
		if (_size > 0)
			--_size;
	}

	T& Back()
	{
		assert(_size > 0);
		return _a[_size - 1];
	}

	size_t Size()
	{
		return _size;
	}

	bool Empty()
	{
		return _size == 0;
	}
	void Print();
	void _CheckCapacity()
	{
		if (_size >= _capacity)
		{
			if (TypeTraits <T>::__IsPODType().Get())
			{
				cout << "这里是内置类型扩容的情况" << endl;
				_a = (T*)realloc(_a, (_capacity * 2 + 3)*sizeof(T));
				_capacity = _capacity * 2 + 3;
			}
			else
			{
				if (_a == NULL)
				{
					_a = new T[3];
				}
				cout << "这里是非内置类型扩容的情况" << endl;
				_capacity = _capacity * 2 + 3;
				T* tmp  = new T[_capacity];
				if (_a)
				{
					for (size_t i = 0; i < _size; ++i)
					{
						tmp[i] = _a[i];
					}
					delete[] _a;
					_a = tmp;
				}
			}
		}
	}
protected:
	T* _a;
	size_t _size;
	size_t _capacity;
};
template<class T>
void SeqList<T>::Print()
{
	for (size_t i = 0; i < _size; ++i)
	{
		cout << _a[i] << " ";
	}
	cout << endl;
}

void  Test()
{
	SeqList<int> a;
	a.PushBack(1);
	a.PushBack(2);
	a.PushBack(3);
	a.PushBack(9999999);
	a.Print();



	SeqList<string> b;
	b.PushBack("a");
	b.PushBack("b");
	b.PushBack("c");
	b.PushBack("zhangchenliang");
	b.Print();
}

现在我们再从调试的角度来看,程序的调用过程:
我的第一个断点打到需要扩容开始的地方:
现在走到这里,走进扩容函数:
因为开辟的是一个内置类型的空间,在if语句里程序跳转到了_TureTyped的Get()函数里面。
现在已经知道表达式为真,所以走的是if里面的开辟空间的方法。
这就是程序的基本运行过程,当模板参数为自定义类型时过程也是相似的,只不过最后走的是else里面的代码而已。
这就是一个最简单的类型萃取思想,你只要理解它在以后的各种各样的场合碰到,不同类型不同方法的时候能够 想到
类型萃取并灵活运用它,这就算你学会它了~ 它的思想很简单,就是兵来将挡,水来土掩。使用了类型萃取 让你代码
多一丝从容.(这里的特化一定要理解清楚,里面还有偏特化也很重要)。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ — 类型萃取 的相关文章

  • C# 中的通用 foreach 循环

    给出以下代码的编译器告诉我 使用未分配的局部变量 x 有什么想法吗 public delegate Y Function
  • 使 C++ 在模板函数的特定实例化上编译失败

    我正在开发一个具有模板功能的项目 如下所示 template
  • Task.Delay 是否真的像 I/O 操作一样异步,即它依赖于硬件和中断而不是线程?

    我发现了大量相关内容 但这些内容都是拐弯抹角的 但我始终无法找到答案 我几乎 100 确定Task Delay int 不使用线程 因为我可以在只有 16 个逻辑处理器的机器上运行此代码 var tasks new List
  • 为什么 Resources.Load 返回 null?

    我的项目有多个精灵 位于 Assets Sprites 中 我想使用 C 脚本加载它们 我已经测试过这个 Sprite myFruit Resources Load
  • Xamarin 中 QR 扫描后的处理对话框

    我在Xamarin应用程序中使用QR码扫描仪 当它扫描QR码时 它会执行一些操作 大约需要一分钟 而在执行操作时 我想在屏幕上显示一个加载对话框 但是 它没有显示在屏幕上 并且在应用程序的其他地方 它运行得很好 Code var expec
  • 未初始化成员的警告在 C++11 上消失

    我编译这个简单的程序 include
  • 如何吞咽……有具体原因的异常

    在这个方法中 public static void Detach try using var master new DataContext Data Source LocalDB MSSQLLocalDB Initial Catalog m
  • 同步和异步 API

    我正在开发一个库 它提供一些耗时的服务 我需要每个 API 有两个版本 一个用于同步函数调用 另一个用于异步 图书馆用户应决定使用哪个版本 服务结果可能对于系统继续运行 同步调用 至关重要 可能需要在不同的工作线程中完成相同的操作 因为结果
  • 将supportedRuntime嵌入到exe文件中

    我需要将仅包含supportedRuntime 设置的app config 文件嵌入到我的exe 文件中 我尝试执行构建操作嵌入资源 但它现在没有从配置文件中读取值 并且它不起作用 这是我的配置文件
  • 只允许在 datagridview 单元格中键入一些字符

    有没有办法只将某些字符添加到 datagridview 单元格中 像 1234567890 据我所知 您可以使用两种方法来实现此目的 第一个 我认为最好的 是使用 CellValidating 事件DataGridView并检查输入的文本是
  • 使用箭头键滚动可滚动控件

    我正在使用一个ScrollableControl在我的 C 项目中 我想知道如何将箭头键映射到垂直 水平滚动 编辑 我的图片框获得焦点 并且我设法映射滚动键 这里的问题是 当我按下箭头键时 它会滚动一次 然后失去焦点 将其交给滚动查看器旁边
  • 为什么不能使用 C# 对象初始值设定项语法调用方法? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • C++ 标准是否保证未使用的私有字段会影响 sizeof?

    考虑以下结构 class Foo int a 在 g 中测试 我明白了sizeof Foo 4但这是由标准保证的吗 是否允许编译器注意到a是一个未使用的私有字段并将其从类的内存表示中删除 导致更小的 sizeof 我不希望任何编译器真正进行
  • 如何在 .NET Core 中设置全局环境变量(用户范围或系统范围)

    在完整的 NET中我们可以通过EnvironmentVariableTarget枚举到Environment SetEnvironmentVariable call public enum EnvironmentVariableTarget
  • 绑定到 ListView 项目从视图模型中点击的属性

    我正在尝试使用 itemtapped 属性将事件绑定到菜单页面上的 ListView 目前我在我的应用程序中使用 MVVM Xamarin 表单实验室 框架 我想要完成的是当用户点击菜单项时应用程序导航到正确的视图 这是xaml代码
  • Qt 对象的生命周期

    Qt 对象的生命周期是多少 Such as QTcpSocket socket new QTcpSocket 套接字什么时候会被销毁 我应该使用 delete socket 有什么区别吗 QTcpSocket socket 我找不到有关此的
  • 指针 (*argv[]) 的指针的指针算术?

    我知道foo bar 等于 foo bar 但是什么是 foo bar 等于 例如访问 argv 2 我对这一点的理解有些困惑 我认为可能是这样的 foo bar 但我不确定 如果这是一个简单的答案 我深表歉意 a b 相当于 a b 由于
  • 使用 MVVM 绑定 Xamarin.Forms 中的属性

    我在使用 Xamarin Forms 和 MVVM 制作游戏时遇到问题 游戏中有一艘由用户控制的潜艇 并且有水雷掉落 因此用户必须避开这些水雷 这些地雷是在运行时使用 2 个计时器生成的 因此我用 XAML 中的 CollectionVie
  • C# StreamReader 使用分隔符保存到数组

    我有一个文本文件 其中包含制表符分隔的数据 我在 C 应用程序中需要的是从文本文件中读取一行并将它们保存到一个数组中 在每个位置将它们分开 t 然后我对下一行做同样的事情 My code StreamReader sr new Stream
  • 提高大型结构列表的二进制序列化性能

    我有一个以 3 个整数保存 3d 坐标的结构 在测试中 我将 100 万个随机点放在一起 List 然后对内存流使用二进制序列化 内存流大小约为 21 MB 这似乎非常低效 因为 1000000 点 3 坐标 4 字节应该至少为 11MB

随机推荐

  • 利用Repeater控件显示主-从关系数据表

    本文介绍了一种利用Repeater控件显示主 从关系数据表的方法 原文参见 http support microsoft com default aspx scid kb en us 306154 NestedRepeater aspx
  • Lora配置

    文章目录 前言 一 LoRa是什么 二 使用步骤 总结 前言 随着物联网技术的发展 越来越多的应用场景需要进行数据传输和通信 而LoRa技术的出现 为长距离 低功耗 低速率的通信提供了一种可靠的解决方案 正点原子的LoRa模块 是一种基于S
  • Oracle 输出方式

    用过几次经常忘记 特此做个笔记 1 Oracle 输出没有sqlserver方便 直接print message 就行 而是需要放到 块 中输出 比如下面的匿名块 set serveroutput on declare begin dbms
  • pcm vlc 文件_VLC播放pcm

    Applications VLC app Contents MacOS VLC demux rawaud rawaud channels 1 rawaud samplerate 48000 rawaud fourcc s8 Users lz
  • 白银价格波动因素是什么?

    在白银产品的投资中 人们要更加全面的对市场进行了解 其中涉及到的范围比较广阔 只有对整个市场的发展和影响因素进行细致的分析 才可以更好的参与到白银产品的投资中 为了更好的获得实际性的利益 重点要对白银的价格进行细致的了解 这样使人们在投资时
  • 特别篇:input小结

    input 我们日常中在使用Python时都会使用input 在控制台进行输出 但是不知道大家发现没有 因为Python是一种动态语言 所以不会像Java中的Scanner类 在代码中可以控制输入的类型 所以当我们输入数字时 底层也会把我们
  • 程序流程图画法复习-三种循环语句(for,while,do while)

    程序流程图画法复习 1 程序流程图的作用 程序流程图的作用程序流程图的作用程序流程图的作用 程序流程图是人们对解决问题的方法 思路或算法的一种描述 流程图的优点 a 采用简单规范的符号 画法简单 b 结构清晰 逻辑性强 c 便于描述 容易理
  • HTTP请求行详解

    目录 一 认识URL 二 认识方法 2 1 GET方法 2 2 POST方法 2 3 其他方法 请求行也就是HTTP请求的第一行 接下来将对第一行内容进行详细解释 一 认识URL 平时我们俗称的 网址 其实就是说的 URL Uniform
  • 从0开始学PyTorch(一):线性回归、Softmax与分类模型、多层感知机

    文章目录 线性回归 模型 数据集 损失函数 优化函数 随机梯度下降 Softmax与分类感知机 softmax的基本概念 多层感知机 多层感知机的基本知识 隐藏层 表达公式 激活函数 线性回归 线性回归的基本要素 模型 为了简单起见 这里我
  • java文件下载接口,含泪整理面经

    Java如何入门 1 建立好开发环境 首先建立好开发环境非常重要 工欲善其事 必先利其器 做任何开发 首先就是要把这个环境准备好 之后就可以去做各种尝试 尝试过程中就能逐渐建立信心 初学者往往在环境配置中被各种预想不到的问题弄得很沮丧 这里
  • Python工业项目实战 04:数仓维度层DWS层构建

    知识点01 课程回顾 ODS层与DWD层的功能与区别是什么 ODS 原始数据层 存储格式 AVRO 数据内容 基本与原始数据是一致的 DWD 明细数据层 存储格式 Orc 数据内容 基于与ODS层是一致的 ODS层的需求是什么 自动化建库建
  • php怎么判断密码是否达标,php中简单的密码强中弱判断,php密码验证规则

    密码种类 1 小写字母 2 大写字母 3 数字 4 特殊符号 弱 1 中 2 强 3 或 4 长度 6 16个字符 区分大小写 1 长度判断 pwd len strlen password if pwd len gt 16 pwd len
  • 【华为OD机试】仿 LISP 运算【2023 B卷

    华为OD机试 真题 点这里 华为OD机试 真题考点分类 点这里 题目描述 LISP 语言唯一的语法就是括号要配对 形如 OP P1 P2 括号内元素由单个空格分割 其中第一个元素 OP 为操作符 后续元素均为其参数 参数个数取决于操作符类型
  • 百度语音合成精简版

    我参照文档合成的源码下载 https download csdn net download qq 31939617 10388588 下载 文档 Android TTS SDK 集成进入helloworld 项目 一 简介 本文针对遇见集成
  • io流读写文件(文件内容修改)

    package cn sos psasps import java io BufferedReader import java io BufferedWriter import java io FileReader import java
  • Git第十三讲 Git重置和回滚更改

    在使用Git时 我们有时可能会犯错或者需要撤销之前的提交或更改 Git提供了一些强大的命令来重置和回滚更改 帮助我们管理项目的版本控制 本文将介绍如何使用Git进行重置和回滚操作 重置到指定提交 重置 Reset 命令允许我们将分支的指针移
  • tar 打包压缩命令

    tar 命令用于文件的打包或压缩 是最为常用的打包压缩命令 其语法格式如下 tar 选项 文件名 tar gz 源文件 tar czvf xxx tar gz source file tar czvf 包名 tar gz 源文件 以tar
  • 【以太坊系列-005】使用solc编译solidity智能合约,并生产web3deploy格式的代码

    说明 本文主要介绍 只通过solc来进行智能合约的部署 更简单的方式是通过remix直接生成 该场景是用于用户自己修改了solc编译器 remix无法使用的情况 1 代码结构 cat counter sol pragma solidity
  • SPP连接流程

    1 SDP服务注册 Server Channel即RFCOMM Server Channel 表示上层profile的rfcomm channel Central设备会使用SDP查询SPP的Server Channel 在后面使用RFCOM
  • C++ — 类型萃取

    类型萃取 在编程中我们可能时常会听到类型萃取这个高大上的 学术名词 我们今天来探究一下这个高大上的学术名词 到底是何方神圣 先看看官方的解释类型萃取使用模板技术来萃取类型 包含自定义类型和内置类型 的某些特 性 用以判断该类型是否含有某些特