C++新经典模板与泛型编程:萃取技术中的值萃取

传入一种类型,萃取出另外一种类型


#include <iostream>

template<typename T>
struct SumFixedTraits;

template<>
struct SumFixedTraits<char>
{
	using sumT = int;
};

template<>
struct SumFixedTraits<int>
{
	using sumT = __int64;
};

template<typename T>
auto funcsum(const T* begin, const T* end)
{
	using sumT = typename SumFixedTraits<T>::sumT; 
	sumT sum{};
	for (;;)
	{
		sum += (*begin);
		if (begin == end)
			break;
		++begin;
	}
	return sum;
}

int main()
{
	int my_int_array1[] = { 10,15,20 };
	int my_int_array2[] = { 1000000000,1500000000,2000000000 };
	char my_char_array[] = "abc";
	
	std::cout << funcsum(&my_int_array1[0], &my_int_array1[2]) << std::endl;
	std::cout << funcsum(&my_int_array2[0], &my_int_array2[2]) << std::endl;
	std::cout << (int)funcsum(&my_char_array[0], &my_char_array[2]) << std::endl;


	return 0;
}

在这里插入图片描述

  • 可以看到,求和的数组元素分别是int类型和char类型,其中char类型数据可以看作范围比较窄的int类型数据。
  • 所以,这两种类型的数据都可以用sumT sum{};的形式进行零初始化,但如果数组元素是其他类型的数据(如一个类类型),那么这种零初始化的格式可能就不合适。
  • 所以,可以尝试着利用萃取技术解决这个问题。在讲解固定萃取(fixed traits)的时候,核心思想是“传入一种类型,萃取出另外一种类型”,那么,此处的值萃取(value traits)的核心思想就是“传入一种类型,萃取出一个值”。
  • 从这句话读者可以看到,传入一种类型后,萃取出来的不仅限于一种类型,也可以是一个值。

传入一种类型,萃取出一个值

  • SumFixedTraits被当作固定萃取类模板,在这里,SumFixedTraits也同时被当作值萃取类模板来使用。现在,试着把零初始化方式移入SumFixedTraits模板,这里需要给每个当前SumFixedTraits模板的特化版本增加一行代码。
template<>
struct SumFixedTraits<char>
{
	using sumT = int;
	static const sumT initValue = 0;
};

template<>
struct SumFixedTraits<int>
{
	using sumT = __int64;
	static const sumT initValue = 0;
};

在上面的代码中,initValue的值是在编译期间就确定好的,可以直接在funcsum()函数模板中使用。修改后的funcsum()代码如下(注意sum被初始化的方式发生了改变)。

// 真他娘滴经典
template<typename T>
auto funcsum(const T* begin, const T* end)
{
	// 传入一个类型,返回一个类型,这就是固定萃取的运行
	using sumT = typename SumFixedTraits<T>::sumT;

	// 传入一个类型,返回一个值,这就是值萃取的运行
	sumT sum = SumFixedTraits<T>::initValue;

	for (;;)
	{
		sum += (*begin);
		if (begin == end)
			break;
		++begin;
	}
	return sum;
}

虽然这种写法目前看不出什么问题,但是如果对一个元素类型为double的数组求和,就会出现问题。设想一下,如果要对double类型的数组元素求和,可以再给SumFixedTraits类模板增加一个特化:

template<>
struct SumFixedTraits<double>
{
	using sumT = double;
	static const sumT initValue = 0.0;
};

error C2864: SumFixedTraits<double>::initValue: 带有类内初始化表达式的静态数据成员必 须具有不可变的常量整型类型,或必须被指定为"内联"
报错的代码行是static const sumT initValue = 0.0;,看上面的报错提示,只有整型才能进行这样的初始化,浮点型不可以。但是使用C++11标准引入的constexpr关键字,发现是可以的。所以,可以修改特化类型为double类型的SumFixedTraits类模板如下。

template<>
struct SumFixedTraits<double>
{
	using sumT = double;
	static constexpr sumT initValue = 0.0;
};

完整的值萃取代码,如下


#include <iostream>

template<typename T>
struct SumFixedTraits;

template<>
struct SumFixedTraits<char>
{
	using sumT = int;
	static const sumT initValue = 0;
};

template<>
struct SumFixedTraits<int>
{
	using sumT = __int64;
	static const sumT initValue = 0;
};

// 真他娘滴经典
template<typename T>
auto funcsum(const T* begin, const T* end)
{
	// 传入一个类型,返回一个类型,这就是固定萃取的运行
	using sumT = typename SumFixedTraits<T>::sumT;

	// 传入一个类型,返回一个值,这就是值萃取的运行
	sumT sum = SumFixedTraits<T>::initValue;

	for (;;)
	{
		sum += (*begin);
		if (begin == end)
			break;
		++begin;
	}
	return sum;
}


template<>
struct SumFixedTraits<double>
{
	using sumT = double;
	static constexpr sumT initValue = 0.0;
};


int main()
{
	double my_double_array1[] = { 12.8,15.8,20.6 };
	std::cout << funcsum(&my_double_array1[0], &my_double_array1[2]) << std::endl;


	return 0;
}

在这里插入图片描述
可以看到,通过值萃取类模板的手段,分别解决了数组元素类型为int、char、double时的求和问题。但是考虑一下,如果数组元素是类类型,如何进行类类型对象的求和呢?例如,有这样一个类A(放在SumFixedTraits类模板前面):

class A
{
public:
	A(int v1, int v2) : m_i(v1 + v2)
	{
	}
	int m_i;
};

template<typename T>
struct SumFixedTraits;

template<>
struct SumFixedTraits<A>
{
	using sumT = A;
	static const sumT initValue(0, 0);
};

编译一下,编译器会报错:
error C2131: 表达式的计算结果不是常数
说明这种初始化initValue对象的方式不可行,那么这个初始化就要放到类外。
(1)对针对类A特化的SumFixedTraits类模板中的static const sumT initValue{0,0 };代码行进行修改,代码如下。

template<>
struct SumFixedTraits<A>
{
	using sumT = A;
	static const sumT initValue; // 这是声明,不是定义
};

(2)然后在某个.cpp源文件中对initValue进行初始化,代码如下(注意写法)。
A const SumFixedTraits<A>::initValue = A{ 0,0 }; //定义并初始化
注意,不能在多个.cpp源文件中都加入上述代码,否则链接会报错,类似:
MyProject.obj : error LNK2005: "public: static class A const SumFixedTraits<class A>:: initValue" (?initValue@?$SumFixedTraits@VA@@@@2VA@@B) 已经在 myclass.obj 中定义

// A.h
#pragma once

class A
{
public:
	A(int v1, int v2) : m_i(v1 + v2)
	{
	}
	int m_i;
};

// A.cpp
#include "A.h"
#include "test.h"

A const SumFixedTraits<A>::initValue = A(0, 0); // 定义并初始化

// test.h

#include <iostream>

#include "demo.h"

template<typename T>
struct SumFixedTraits;

template<>
struct SumFixedTraits<A>
{
	using sumT = A;
	static const sumT initValue; // 这是声明,不是定义
};

template<typename T>
auto funcsum(const T* begin, const T* end)
{
	// 传入一个类型,返回一个类型,这就是固定萃取的运行
	using sumT = typename SumFixedTraits<T>::sumT;

	// 传入一个类型,返回一个值,这就是值萃取的运行
	sumT sum = SumFixedTraits<T>::initValue;

	for (;;)
	{
		sum += (*begin);
		if (begin == end)
			break;
		++begin;
	}
	return sum;
}

int main()
{
	return 0;
}

上述方
案虽然可以解决问题,但比较烦琐,在C++ 17标准中,提出了一种新的解决方案解决类似问题—inline变量。所以,修改后的针对类A特化的SumFixedTraits类模板代码如下(注意写法)。

template<>
struct SumFixedTraits<A>
{
	using sumT = A;
	//static const sumT initValue; // 这是声明,不是定义
	inline static const sumT initValue(0, 0);
};

除了上述两种解决方案,还可以采用在特化的SumFixedTraits类模板中引入静态成员函数(initValue)的方式解决问题,因为这种函数都写在特化的SumFixedTraits类模板定义中,因此都是inline的成员函数。看一看修改后的各个特化的SumFixedTraits类模板,代码如下。

// killCmake.cpp: 定义应用程序的入口点。
//

#include "killCmake.h"

using namespace std;

class A
{
public:
	A(int v1, int v2) : m_i(v1 + v2)
	{
	}
	int m_i;
};

template<typename T>
struct SumFixedTraits;

template<>
struct SumFixedTraits<A>
{
	using sumT = A;
	//static const sumT initValue; // 这是声明,不是定义
	static const sumT initValue()
	{
		return A(0, 0);
	}
};

template<>
struct SumFixedTraits<char>
{
	using sumT = int;
	static const sumT initValue()
	{
		return 0;
	}
};

template<>
struct SumFixedTraits<int>
{
	using sumT = __int64;
	static const sumT initValue()
	{
		return 0;
	}
};


template<>
struct SumFixedTraits<double>
{
	using sumT = double;
	static constexpr sumT initValue()
	{
		return 0;
	}
};

// 真他娘滴经典
template<typename T>
auto funcsum(const T* begin, const T* end)
{
	// 传入一个类型,返回一个类型,这就是固定萃取的运行
	using sumT = typename SumFixedTraits<T>::sumT;

	// 传入一个类型,返回一个值,这就是值萃取的运行
	sumT sum = SumFixedTraits<T>::initValue();

	for (;;)
	{
		sum += (*begin);
		if (begin == end)
			break;
		++begin;
	}
	return sum;
}

int main()
{
	cout << "Hello CMake." << endl;
	return 0;
}

这个程序到这里还没完,函数模板funcsum()中要对类A的对象作加法运算,用到了+=运算符,所以这里需要对类A重载一下+=运算符。在类A的定义中添加代码:

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

在main()主函数中添加代码:

// killCmake.cpp: 定义应用程序的入口点。
//

#include "killCmake.h"

using namespace std;

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

template<typename T>
struct SumFixedTraits;

template<>
struct SumFixedTraits<A>
{
	using sumT = A;
	//static const sumT initValue; // 这是声明,不是定义
	static const sumT initValue()
	{
		return A(0, 0);
	}
};

template<>
struct SumFixedTraits<char>
{
	using sumT = int;
	static const sumT initValue()
	{
		return 0;
	}
};

template<>
struct SumFixedTraits<int>
{
	using sumT = __int64;
	static const sumT initValue()
	{
		return 0;
	}
};


template<>
struct SumFixedTraits<double>
{
	using sumT = double;
	static constexpr sumT initValue()
	{
		return 0;
	}
};

// 真他娘滴经典
template<typename T>
auto funcsum(const T* begin, const T* end)
{
	// 传入一个类型,返回一个类型,这就是固定萃取的运行
	using sumT = typename SumFixedTraits<T>::sumT;

	// 传入一个类型,返回一个值,这就是值萃取的运行
	sumT sum = SumFixedTraits<T>::initValue();

	for (;;)
	{
		sum += (*begin);
		if (begin == end)
			break;
		++begin;
	}
	return sum;
}

int main()
{
	cout << "Hello CMake." << endl;

	A my_A_array1[]{ A{3,1},A{6,8},A{12,11} };
	std::cout << funcsum(&my_A_array1[0], &my_A_array1[2]).m_i << std::endl;

	return 0;
}

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【华为网络-配置-025】- 同 VLAN 下不同网段通信(启用 Sub 地址)

要求&#xff1a; 1、各接口配置 VLAN 后配置 Sub 地址使 PC1 与 PC3 通信。 一、sub 地址配置 [LSW1]vlan 10 [LSW1]port-group group-member GigabitEthernet 0/0/1 to GigabitEthernet 0/0/2 [LSW1-port-group]port link-type access [LSW1-port-group]port default vla…

2023年【起重机械指挥】考试题库及起重机械指挥考试内容

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 起重机械指挥考试题库根据新起重机械指挥考试大纲要求&#xff0c;安全生产模拟考试一点通将起重机械指挥模拟考试试题进行汇编&#xff0c;组成一套起重机械指挥全真模拟考试试题&#xff0c;学员可通过起重机械指挥…

Node.js版本管理工具NVM(Node Version Manager)的使用

nvm简介 nvm&#xff08;Node Version Manager&#xff09;是一个用于管理 Node.js 版本的工具。它可以让你在同一台计算机上安装并切换多个 Node.js 版本&#xff0c;非常方便。 如何安装 nvm 下载 nvm 安装包 访问 nvm下载地址 &#xff0c;根据你的操作系统选择对应的安…

“触底”来临?!看看专家怎么说!鼎捷2023年H2半导体行业观察报告火热出炉!

半导体行业周期性“触底”&#xff0c;喊了半年多。如今“底”在哪&#xff1f; 面对业内争议颇多的“V”字拐点是否到来&#xff0c;可以盖棺定论&#xff1f; 不确定的因素依然存在。 但多种迹象表明&#xff0c;半导体行业的回暖趋势已逐渐明朗&#xff01; 用数据说话&…

小型洗衣机什么牌子好又便宜?目前口碑最好的迷你洗衣机

随着大家工作的压力越来越大&#xff0c;下了班之后只能想躺平&#xff0c;在洗完澡之后看着还需要手洗的内衣裤真的很头疼。有些小伙伴还有会攒几天再丢进去洗衣机里面一起&#xff0c;而且这样子是非常不好的&#xff0c;用过的内衣裤长时间不清洗容易滋生细菌&#xff0c;而…

MBA-历年数学条件充分判断

已知p&#xff0c;q 为非零实数. 则能确定的值. (1)pq1 &#xff08;2&#xff09; 1. pq1 ; p 1-q ; ;无法确定 1&#xff1b;q ; * * 1;可以确定 信封中装有10张奖券&#xff0c;只有1张有奖.从信封中同时抽取2张奖券&#xff0c;中奖的概率记为 P;从信…

【NLP】如何管理大型语言模型 (LLM)

什么是LLM编排&#xff1f; LLM 编排是管理和控制大型语言模型 (LLM)的过程&#xff0c;以优化其性能和有效性。这包括以下任务&#xff1a; 提示LLM&#xff1a;生成有效的提示&#xff0c;为LLMs提供适当的背景和信息以产生所需的输出。链接LLM&#xff1a; 结合多个LLM的输…

决战排序之巅(一)

决战排序之巅 插入排序直接插入排序 void InsertSort(int* arr, int n)希尔排序 void ShellSort(int* arr, int n)测试插入排序测试函数 void verify(int* arr, int n)测试 InsertSort测试 ShellSort测试速度 InsertSort & ShellSort 选择排序直接选择排序 void SelectSort…

生命在于折腾——使用PD打开OVA格式虚拟机

一、前言 下载了一个封装的工具箱虚拟机&#xff0c;格式是OVA的&#xff0c;PD无法直接打开&#xff0c;之前成功转换后打开过&#xff0c;但那时候没有记录&#xff0c;今天记录一下。 二、过程 有两种方法 1、去vmware官网下载工具VMware OVF Tool 地址&#xff1a;htt…

Spatial Data Analysis(四):空间自相关示例

Spatial Data Analysis&#xff08;四&#xff09;&#xff1a;空间自相关示例 空间自相关是地理信息科学&#xff08;GIS&#xff09;和空间统计学中的重要概念之一&#xff0c;用于研究地理空间上的数据变异性和相关性。空间自相关分析的目标是探讨地理空间中的现象是否呈现…

8路编码器脉冲信号测量或16路DI高速计数器,Modbus RTU模块 YL69

特点&#xff1a; ● 编码器解码转换成标准Modbus RTU协议 ● 可用作编码器计数器或者转速测量 ● 支持8个编码器同时计数&#xff0c;可识别正反转 ● 也可以设置作为16路独立DI高速计数器 ● 编码器计数值支持断电自动保存 ● DI输入和电源之间3000V隔离 ● 通过RS-4…

【Java基础系列】JavaWeb入门

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

GUI的简单概述和基本使用

GUI的概念 1&#xff0c;到目前为止&#xff0c;我们编写的都是控制输入的程序&#xff0c;操作使用非常不直观&#xff0c;采取一直方式让效果呈现在窗口上。 2&#xff0c;GUI及图形界面指采用图像方式显示的用户界面&#xff0c;与早期计算机的命令行界面相比&#xff0c;…

【征稿倒计时十天】第三届高性能计算与通信工程国际学术会议(HPCCE 2023)

【有ISSN、ISBN号&#xff01;&#xff01;往届均已完成EI检索】 第三届高性能计算与通信工程国际学术会议(HPCCE 2023) 2023 3rd International Conference on High Performance Computing and Communication Engineering (HPCCE 2023) 2023年12月22-24日 | 中国哈尔滨 第三…

Web组态与传统组态有什么区别?探索云组态的革新

一、Web组态的定义和背景 在深入探讨之前&#xff0c;我们先回顾一下“组态”的定义。在工业自动化领域&#xff0c;组态软件是用于创建监控和数据采集&#xff08;SCADA&#xff09;系统的工具&#xff0c;它允许工程师构建图形界面&#xff0c;实现与各种设备和机器的数据交…

速达软件任意文件上传漏洞复现

简介 速达软件专注中小企业管理软件,产品涵盖进销存软件,财务软件,ERP软件,CRM系统,项目管理软件,OA系统,仓库管理软件等,是中小企业管理市场的佼佼者,提供产品、技术、服务等信息,百万企业共同选择。速达软件全系产品存在任意文件上传漏洞,未经身份认证得攻击者可以通过此漏…

微服务2 Docker学习 P42-P60

Docker学习视频https://www.bilibili.com/video/BV1LQ4y127n4?p42&vd_source8665d6da33d4e2277ca40f03210fe53a 文档资料: 链接&#xff1a;https://pan.baidu.com/s/1P_Ag1BYiPaF52EI19A0YRw?pwdd03r 提取码&#xff1a;d03r Docker 其他笔记 服务器容器化-docker(全…

Python第三次练习

Python 一、如何判断一个字符串是否是另一个字符串的子串二、如何验证一个字符串中的每一个字符均在另一个字符串中出现三、如何判定一个字符串中既有数字又有字母四、做一个注册登录系统 一、如何判断一个字符串是否是另一个字符串的子串 实现代码&#xff1a; string1 inp…

5G - NR物理层解决方案支持6G非地面网络中的高移动性

文章目录 非地面网络场景链路仿真参数实验仿真结果 非地面网络场景 链路仿真参数 实验仿真结果 Figure 5 && Figure 6&#xff1a;不同信噪比下的BER和吞吐量 变量 SISO 2x2MIMO 2x4MIMO 2x8MIMOReyleigh衰落、Rician衰落、多径TDL-A(NLOS) 、TDL-E(LOS)(a)QPSK (b)16…

【React Hooks】useReducer()

useReducer 的三个参数是可选的&#xff0c;默认就是initialState&#xff0c;如果在调用的时候传递第三个参数那么他就会改变为你传递的参数&#xff0c;实际开发不建议这样写。会增加代码的不可读性。 使用方法&#xff1a; 必须将 useReducer 的第一个参数&#xff08;函数…
最新文章