- 了解标准库中许多萃取技术的实现方法
- 灵活运用并组合这些实现方法,写出 功能更强大、更优雅和实用的代码
固定萃取技术
#include <iostream>
// 固定萃取技术
template<typename T>
T funcsum(const T* begin, const T* end)
{
T sum{}; // 数值类型初始化为0;指针型变量初始化为nullptr;布尔类型变量初始化为false
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 }; // int类型的最大值在21亿至22亿之间
char my_char_array[] = {"abc"}; // 97 ,98, 99
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;
}
上述代码会实例化出以下两个funcsum函数:
int funcsum<int>(const int*,const int*)
char funcsum<char>(const char*,const char*)
// 也就是说funcsum()函数模板的类型模板T一次被推断成了int类型,一次被推断成了char类型。
// 所以从结果中可以看出,只有第一个结果正确,第二个和第三个结果都不正确,因为int类型能够保存的范围
// 为21亿多一点,不到22亿,但是my_int_array2的相加结果为45亿,所以出现了溢出,得出了205032704(溢出
// 的规则,极其重要)。第三个结果为char类型对应的ASCII码相加,所以得出97+98+99=294,但是char类型能够存
// 储的最大数值为127,所以结果值溢出,得到38.
如何解除保存的结果值溢出这个问题呢?
- 其实也比较好想象,如果用一个__int64类型(也叫long long类型或int64_t类型)的变量保存前两个数组(myintarray1和myintarray2)元素的和值,肯定是能够保存下的;如果用一个int类型的变量保存最后一个数组(mychararray)元素的和值,肯定也是能够保存下的。
- 那么可以修改一下funcsum()函数模板,新引入一个类型模板参数U,用于指定结果类型,这里为了代码书写方便,把新引入的类型模板参数U放在模板参数列表的前面。
#include <iostream>
// 固定萃取技术
template<typename T>
T funcsum(const T* begin, const T* end)
{
T sum{}; // 数值类型初始化为0;指针型变量初始化为nullptr;布尔类型变量初始化为false
for (;;) {
sum += (*begin);
if (begin == end)
break;
++begin;
}
return sum;
}
template<typename U,typename T>
U funcsum(const T* begin, const T* end)
{
U 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 }; // int类型的最大值在21亿至22亿之间
char my_char_array[] = {"abc"}; // 97 ,98, 99
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;
std::cout << std::endl;
std::cout << funcsum<__int64>(&my_int_array1[0], &my_int_array1[2]) << std::endl;
std::cout << funcsum<__int64>(&my_int_array2[0], &my_int_array2[2]) << std::endl;
std::cout << (int)(funcsum<int>(&my_char_array[0], &my_char_array[2])) << std::endl;
return 0;
}
上述代码会实例化以下两个funcsum()函数
__int64 funcsum<__int64,int>(int const *,int const *)
int funcsum<int,char>(char const *,char const *)
- 上述结果就是对的了。虽然结果问题解决了,但显然这种解决方案不太方便,每次调用funcsum()函数时都需要指定额外的模板参数。
- 有没有更好的解决方案呢?有,通过写一个trait类模板来解决,这里要写的trait类模板就是一个固定萃取类模板。简单地说,固定萃取类模板在这里就像一个中间件一样,夹在main()主函数中对funcsum()的调用代码与函数模板funcsum()代码之间,实现的功能如下。
(1). 当传入一个int类型(int类型数组myintarray1和myintarray2元素的类型),固定萃取类模板可以返回一个__int64类型(数组元素的和值类型)用来保存数组元素和值。
(2). 当传入一个char类型(char类型数组mychararray元素的类型),固定萃取类模板可以返回一个int类型(数组元素的和值类型)用来保存数组元素和值。
(3). 固定萃取类模板的“传入一种类型,萃取出(得到)另外一种类型”。这样,也就不需要在funcsum()函数模板中引入类型模板参数U了。而且这个固定萃取类模板可以在多个场合被复用。
// 固定萃取类模板的泛化版本
template<typename T>
struct SumFixedTraits;// 不需要实现代码,因为不需要用该版本进行实例化
// 各个固定萃取类模板的特化版本
// (1)传入的是char类型时,返回的是int类型
template<>
struct SumFixedTraits<char> // char表示给进来的是char类型
{
using sumT = int;
};
// (2)传入的是int类型时,返回的是__int64(long long/int64_t)类型
template<>
struct SumFixedTraits<int> // int表示传入的是int类型
{
using sumT = __int64; // sumT为__int64类型,代表返回的是一个__int64类型
};
/* (3)... 其他传入的某个类型,返回的是另外一个类型,可以任意扩展出多个sumFixedTraits类模板的特化版本*/
有了SumFixedTraits类模板(是一个trait类模板)的各种特化版本之后,就可以借助这些特化版本重新改写前面求数组中元素和值的funcsum()函数模板。改写后的代码如下。
#include <iostream>
// 固定萃取类模板的泛化版本
template<typename T>
struct SumFixedTraits;// 不需要实现代码,因为不需要用该版本进行实例化
// 各个固定萃取类模板的特化版本
// (1)传入的是char类型时,返回的是int类型
template<>
struct SumFixedTraits<char> // char表示给进来的是char类型
{
using sumT = int;
};
// (2)传入的是int类型时,返回的是__int64(long long/int64_t)类型
template<>
struct SumFixedTraits<int> // int表示传入的是int类型
{
using sumT = __int64; // sumT为__int64类型,代表返回的是一个__int64类型
};
/* (3)... 其他传入的某个类型,返回的是另外一个类型,可以任意扩展出多个sumFixedTraits类模板的特化版本*/
template<typename T>
auto funcsum(const T* begin, const T* end)
{
using sumT = typename SumFixedTraits<T>::sumT; // 传入一个类型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 }; // int类型的最大值在21亿至22亿之间
char my_char_array[] = {"abc"}; // 97 ,98, 99
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;
}
通过这个范例,不难看到固定萃取类模板(SumFixedTraits)起到的作用—通过某个类型,得到另外一个类型,这是模板与泛型编程很常见的一种应用。
嘛嘞个碧池,终于把using a = typename classx<T>::outputtype
写完整了,之前尝试过几次,均以失败告终。