【萃取技术】萃取技术中的值萃取

一、一个常规范例

之前在:固定萃取 这一章节介绍了 f u n c s u m funcsum funcsum的实现,对于 s u m T sumT sumT的零初始化,可以通过本章节的值萃取来优化。

回顾一下之前的代码:

//使用萃取技术来简化模板

//泛化版本
template<typename T>
struct SumFixedTraits; //只需要声明

//char类型特化版本
template<>
struct SumFixedTraits<char> {
	using sumT = int;
};

//int类型特化版本
template<>
struct SumFixedTraits<int> {
	using sumT = long long;
};

//float类型特化版本
template<>
struct SumFixedTraits<float> {
	using sumT = double;
};

//改造后的funcsum

//使用别名模板
template<typename T>
using sumT = typename SumFixedTraits<T>::sumT;

template<typename T>
sumT<T> funcsum2(const T* begin, const T* end) {

	sumT<T> sum{};

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

现在,我们对 s u m { } sum\{\} sum{}这样的零初始化进行优化,优化方式就是通过值萃取的方法。

值萃取的方法之前在数组萃取的时候就是用过,萃取出数组的大小。

这里的初始化类似,我们对SumFixedTraits类内添加上静态变量就可以进行萃取值了。

如下:

//值萃取

//泛化类型
template<typename T>
struct SumFixedTraits;

//特化版本1
template<>
struct SumFixedTraits<char> {
	using sumT = int;
	static const sumT initValue = 0;
};

//特化版本2
template<>
struct SumFixedTraits<int> {
	using sumT = long long;
	//sumT类型的初始值

	static const sumT initValue = 0;
};

//特化版本3
template<>
struct SumFixedTraits<double> {
	using sumT = double;
	static constexpr sumT initValue = 0.0; //这里不能使用const
};

注意,在 d o u b l e double double内部不能使用const,因为static修饰的成员能用const修饰的为 i n t 、 u n s i g n e d i n t int、unsigned int intunsignedint等类型,而 d o u b l e 、 f l o a t double、float doublefloat类型需要使用 c o n s t e x p r constexpr constexpr修饰。


求和函数 f u n c s u m funcsum funcsum内部也需要改变:

//求和函数
template<typename T>
auto funcsum(const T* begin, const T* end) {
	using sumT = typename SumFixedTraits<T>::sumT;
	sumT sum = SumFixedTraits<T>::initValue;

	for (auto it = begin; it != end; ++it) {
		sum += *it;
	}

	return sum;
}

这样,就能保证初始化时的数值是正确的,并且可以指定。

实际上,这里面的静态成员初始化的值也可以替换为静态成员函数。

以下是测试代码:

void Test1() {
	char arr1[] = { 'a','b','c' };
	int arr2[] = { 10000000,20000000,30000000 };
	double arr3[] = { 1.0,2.0,3.0 };

	std::cout << funcsum(&arr1[0], &arr1[2]) << std::endl;
	std::cout << funcsum(&arr2[0], &arr2[2]) << std::endl;
	std::cout << funcsum(arr3, arr3 + 2) << std::endl;

}

运行结果如下:
在这里插入图片描述

二、自定义类型初始化

使用值萃取的方式的好处就是可以自定义初始化内容,例如,有以下的类:

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

我们补充SumFixedTraits类对 A A A类型的特化版本:

//对A类型的特化版本
template<>
struct SumFixedTraits<A> {
	using sumT = A;
	static const sumT initValue; //必须类内定义类外声明

};

const A SumFixedTraits<A>::initValue = A{ 0,0 }; //类外声明

注意这里不能再类内初始化,需要在类外初始化 i n i t V a l u e initValue initValue

或者在 C + + 17 C++17 C++17之后,使用inline static关键字,可以在类内初始化,如 B B B类型:

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

template<>
struct SumFixedTraits<B> {
	using sumT = B;
	inline static const sumT initValue{ 0,0 };
};


最后,由于存在加法操作,我们需要重载一下operator +,最终 A 、 B A、B AB类代码如下:

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

	//重载operator+=
	A& operator +=(const A& obj) {
		m_i += obj.m_i;
		return *this;
	}
};

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

	//重载operator+=
	B& operator +=(const B& obj) {
		m_i += obj.m_i;
		return *this;
	}
};

测试代码如下

void Test2() {
	A arr[] = { A{1,1},A{2,2},A{3,3} };
	std::cout << funcsum(arr, arr + 3).m_i << std::endl;

	B arr2[] = { B{ 1,1 }, B{ 2,2 }, B{ 3,3 } };

	std::cout << funcsum(&arr2[0], &arr2[3]).m_i << std::endl;
}

成功编译运行,结果如下:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值