模版/模版函数的全特化/偏特化与类型萃取

使用模版需要对不同的类型作不同的处理时,需要模版萃取
同时,模版/模版函数的全特化/偏特化 是一个易混淆的知识点,整理如下

一、模版的全特化、偏特化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
template<typename T1, typename T2>  
class Test
{
public:
Test(T1 i,T2 j):a(i),b(j){cout<<"模板类"<<endl;}
private:
T1 a;
T2 b;
};

template<>
class Test<int , char>
{
public:
Test(int i, char j):a(i),b(j){cout<<"全特化"<<endl;}
private:
int a;
char b;
};

template <typename T2>
class Test<char, T2>
{
public:
Test(char i, T2 j):a(i),b(j){cout<<"偏特化"<<endl;}
private:
char a;
T2 b;
};

调用时,依次对应上述代码

1
2
3
Test<double , double> t1(0.1,0.2);  //调用模版类
Test<int , char> t2(1,'A'); //调用模版类的全特化
Test<char, bool> t3('A',true); //调用模版类的偏特化

二、模版函数的全特化/偏特化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//模板函数  
template<typename T1, typename T2>
void fun(T1 a , T2 b)
{
cout<<"模板函数"<<endl;
}

//全特化
template<>
void fun<int ,char >(int a, char b)
{
cout<<"全特化"<<endl;
}

//函数不存在偏特化:下面的代码是错误的
/*
template<typename T2>
void fun<char,T2>(char a, T2 b)
{
cout<<"偏特化"<<endl;
}
*/

三、类型萃取

问题提出

存在以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
template <class T>
class SeqList
{
public:
SeqList()
:_a(NULL)
, _size(0)
, _capacity(0)
{}

void CheckCapacity()
{
if (_size >= _capacity)
{
_capacity = _capacity > 0 ? _capacity * 2 : 3;
T* newA = new T[_capacity];
if (_a != NULL)
{
memcpy(newA, _a, _size*sizeof(T));//使用memcpy
}
delete[] _a;
_a = newA;

}
}

void PushBack(const T& x)
{
CheckCapacity();
_a[_size++] = x;
}

void Print()
{
for (size_t i = 0; i < _size; i++)
{
cout << _a[i] << " ";
}
cout << endl;
}

private:
T* _a;
size_t _size;
size_t _capacity;
};

如果类型为int,则上述代码不会存在问题。
如果类型为string,则上述代码在运行时程序会崩溃

问题分析

string在设计增容的时候,必须调用构造函数,不能简单的使用memcpy,(必须深拷贝),因此在memcpy的时候会造成野指针的访问。
一个有效的解决方案时全部使用new,则对string这种类型作处理的时候,编译器会调用构造函数。

遗憾的是,在实际应用场景下,使用for 循环+new虽然可以避免崩溃,但却会大幅度牺牲效率(memcpy效率比其快得多)

一个改进版的解决方案是使用全特化
对于string

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
template <>
class SeqList<string>
{
public:
SeqList()
:_a(NULL)
, _size(0)
, _capacity(0)
{}

void CheckCapacity()
{
if (_size >= _capacity)
{
_capacity = _capacity > 0 ? _capacity * 2 : 3;
string* newA = new string[_capacity];
if (_a)
{
for (size_t i = 0; i < _size; i++)
{
newA[i] = _a[i];
}
delete[] _a;
}
_a = newA;
}
}

void PushBack(const string& x)
{
CheckCapacity();
_a[_size++] = x;
}

~SeqList()
{
delete[] _a;
_size = _capacity = 0;
}

private:
string* _a;
size_t _size;
size_t _capacity;
};

一种类型就要这么一大段代码,极大的降低了复用性,同时难以考虑全面对所有类型做处理。
类型萃取则解决了这种问题,兼顾代码复用与效率。

类型萃取

对于一个模版实现的复制的函数如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template <class T>
T* __TypeCopy(T* dst, const T* src, size_t n,__TrueType)
{
cout << "memcpy()" << endl;
return (T*)memcpy(dst, src, n*sizeof(T));
}

template <class T>
T* __TypeCopy(T* dst, const T* src, size_t n, __FalseType)
{
for (size_t i = 0; i < n; i++)
{
dst[i] = src[i];
}
cout << "operator=()" << endl;
return dst;
}

对于以上代码,对_TypeCopy函数做重载,只要能自动匹配参数TrueType/FalseType即可,为了匹配合适参数,做以下处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct __TrueType//一个空类
{};

struct __FalseType//空类
{};

template <class T>
struct __TypeTraits
{
typedef __FalseType ISPODType;
};

template <>
struct __TypeTraits<int>
{
typedef __TrueType ISPODType;
};

template <class T>
T* TypeCopy(T* dst, const T* src, size_t n)
{
return __TypeCopy(dst, src, n, __TypeTraits<T>::ISPODType());
}

对于以上代码:
当类型为int,TypeCopy函数在返回时最后一段代码__TypeTraits<T>::ISPODType(),ISPODType()会生成一个匿名对象,根据匹配到的结果,会调用合适的__TypeTreits,在类型为int时,会调用

1
2
3
4
5
template <>
struct __TypeTraits<int>
{
typedef __TrueType ISPODType;
};

以上代码将TrueType起别名为ISPODType,因此在调用__TypeTraits<T>::ISPODType()时,返回TrueType,即可匹配合适的处理的函数。
相对的,对于string,会返回参数
FalseType

完整代码如下,萃取实际还是模版的全特化,但基于全特化构造匿名对象使得模版函数匹配到不同的参数以作不同处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
struct __TrueType//一个空类
{};

struct __FalseType//空类
{};

template <class T>
struct __TypeTraits
{
typedef __FalseType ISPODType;
};

template <>
struct __TypeTraits<int>
{
typedef __TrueType ISPODType;
};

template <class T>
T* TypeCopy(T* dst, const T* src, size_t n)
{
return __TypeCopy(dst, src, n, __TypeTraits<T>::ISPODType());
}

template <class T>
T* __TypeCopy(T* dst, const T* src, size_t n,__TrueType)
{
cout << "memcpy()" << endl;
return (T*)memcpy(dst, src, n*sizeof(T));
}

template <class T>
T* __TypeCopy(T* dst, const T* src, size_t n, __FalseType)
{
for (size_t i = 0; i < n; i++)
{
dst[i] = src[i];
}
cout << "operator=()" << endl;
return dst;
}

@张鹏霄 zpx736312737@126.com