一、一个常规范例
之前在:固定萃取 这一章节介绍了 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
int、unsignedint等类型,而
d
o
u
b
l
e
、
f
l
o
a
t
double、float
double、float类型需要使用
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
A、B类代码如下:
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;
}
成功编译运行,结果如下: