类型萃取
依靠的就是模板的特化
,模板的特化
又分为全特化与偏特化
,对于编译器来说,如果你对某一功能有更好的实现,那么就应该使用你的特化版本。
一、函数模板特化
函数模板只有全特化,而没有偏特化
。至于为什么没有偏特化,原因在于已经有了函数重载
,偏特化也没什么用。
有时候并不总是能够写出对所有可能被实例化的类型都合适的模板,在某些情况下,通用模板定义对于某个类型可能是完全错误的,或者不能编译,或者做一些错误的事情。比如下面比较字符串的程序:
#include <iostream>
using namespace std;
template<typename T>
int Compare(T str1, T str2)
{
if (str1 > str2)
return 1;
if (str1 < str2)
return -1;
return 0;
}
int main()
{
char *pStr1 = "abcd";
char *pStr2 = "wert";
cout<<Compare(pStr1, pStr2)<<endl;
system("pause");
return 0;
}
结果如下:
当函数模板不能解决一些问题的时候
,我们引入了模板函数特化
。模板函数特化形式如下:
1)关键字template后面接一对空的尖括号<> 。
2)函数名后接模板名和一对尖括号,尖括号中指定这个特化定义的模板形参。
3)函数形参表
4)函数体
template<>
返回值 函数名<Type>(参数列表)
{
//函数体
}
注意:在模板特化版本的调用
中,实参类型
必须与特化版本函数的形参类型完全匹配
,如果不匹配
,编译器将为实参模板定义中实例化一个实例
。
#include <iostream>
using namespace std;
template<typename T>
T Compare(const T left, const T right)
{
return left > right ? left : right;
}
template<>
char const *Compare<const char*>(const char *const left, const char *const right)
{
if (strcmp(left, right) > 0)
{
return left;
}
return right;}
int main()
{
const char *const str1 = "abcde";
const char *const str2 = "werty";
cout << Compare(str1, str2)<<endl;
char* const str3 = "abcd";
char* const str4 = "wwer";
cout << Compare(str3, str4)<<endl;
system("pause");
return 0;
}
结果如下:
注意:特化不能出现在模板实例的调用之后
,应该在头文件中包含模板特化的声明
,然后使用该特化版本的每个源文件包含该头文件。
二、类模板的全特化与偏特化
全特化与偏特化
是相当于类模板
而言的。全特化是限定死模板实现的具体类型,而偏特化是针对于当模板有多个类型时,限定了其中的一部分。
下面程序是一个模板类的全特化过程
,我将它的模板类型分别限定为int、float:
#include <iostream>
using namespace std;
template<typename T1,typename T2>
class Specialization
{
public:
void FuncTest(T1 _x, T2 _y)
{
cout << "_x:" << _x << endl;
cout << "_y:" << _y << endl;
}
};
//特化标志
template<>
class Specialization<int, float> //全特化
{
public:
void FuncTest(int _x, float _y)
{
cout << "_x:" << _x << endl;
cout << "_y:" << _y << endl;
}
};
int main()
{
Specialization<int, char> _s;
_s.FuncTest(3, 'x');
system("pause");
return 0;
}
结果如下:
模板类型T1与T2明确特化后,当我们实例化对象时,程序自然会去调用这个全特化的类 。一个类被称为全特化的条件:
1、必须有一个主模板类
2、模板类型必须全部明确化
3、类名与主模板类相同
下面是偏特化过程
,在它的模板类型中,存在部分未被明确化的类型:
#include <iostream>
using namespace std;
template<typename T1,typename T2>
class Specialization
{
public:
void FuncTest(T1 _x, T2 _y)
{
cout << "_x:" << _x << endl;
cout << "_y:" << _y << endl;
}
};
//偏特化
template<typename T>
class Specialization<int, T> //类型部分未明确,偏特化
{
public:
void FuncTest(int _x, T _y)
{
cout << "_x:" << _x << endl;
cout << "_y:" << _y << endl;
}
};
int main()
{
Specialization<int, char> _s;
_s.FuncTest(3, 'x');
system("pause");
return 0;
}
结果如下:
我们仅对其中一个模板类型明确化,且存在一个未被明确的类型,这就是偏特化。一个类被称为偏特化的条件:
1、必须有一个主模板类
2、模板类型被部分明确化
3、类名与主模板类相同
注意:对于主模板类、全特化类、偏特化类的调用优先级的次序
为:
全特化类 > 偏特化类 > 主模板类
三、内置类型的特化场景
当我们使用STL顺序表
时,当类型为内置类型时会调用realloc开辟空间
,但是,当类型为自定义类型
时,调用realloc显然是不可以的,这时就应该调用new
来实现。
在STL中的Destroy算法
根据函数的参数类型
的特性:是否具有trivial destructor
【如果用户不定义析构函数,而是用系统自带的,则说明,析构函数基本没有什么用(但默认会被调用)我们称之为trivial destructor。反之,如果特定定义了析构函数,则说明需要在释放空间之前做一些事情,则这个析构函数称为non-trivial destructor。】来选择对应的策略来进行destroy,如果是内置类型
,则不调用该类型的destructor
,否则对迭代器范围内的对象调用destructor来进行destroy。
总结:类型萃取
是在模板的基础上区分内置类型与自定义类型
的,即将内置类型全部特化,然后再进行区分
。
四、POD类型萃取举例
下列程序中,我们利用C++类型萃取
的技巧,可以将内置类型与自定义类型区分开
来,然后对内置类型采用memcpy的方式进行拷贝
,对自定义类型使用for循环拷贝。
#include <iostream>
using namespace std;
//POD:基本类型,指C++中与C兼容的类型
struct _TrueType
{
bool Get()
{
return true;
}
};
struct _FalseType
{
bool Get()
{
return false;
}
};
template<class _Tp>
struct TypeTraits
{
typedef _FalseType _IsPODType;//如果不是内置类型,则IsPODType是FalseType
};
//1.0
template<>
struct TypeTraits<bool>
{
typedef _TrueType _IsPODType; //如果是内置类型则IsPODType是TrueType
};
//2.0
template<>
struct TypeTraits<char>
{
typedef _TrueType _IsPODType; //如果是内置类型则IsPODType是TrueType
};
//3.0
template<>
struct TypeTraits<unsigned char>
{
typedef _TrueType _IsPODType; //如果是内置类型则IsPODType是TrueType
};
//4.0
template<>
struct TypeTraits<short>
{
typedef _TrueType _IsPODType; //如果是内置类型则IsPODType是TrueType
};
//5.0
template<>
struct TypeTraits<unsigned short>
{
typedef _TrueType _IsPODType; //如果是内置类型则IsPODType是TrueType
};
//6.0
template<>
struct TypeTraits<int>
{
typedef _TrueType _IsPODType; //如果是内置类型则IsPODType是TrueType
};
//7.0
template<>
struct TypeTraits<unsigned int>
{
typedef _TrueType _IsPODType; //如果是内置类型则IsPODType是TrueType
};
//8.0
template<>
struct TypeTraits<long>
{
typedef _TrueType _IsPODType; //如果是内置类型则IsPODType是TrueType
};
//9.0
template<>
struct TypeTraits<unsigned long>
{
typedef _TrueType _IsPODType; //如果是内置类型则IsPODType是TrueType
};
//10.0
template<>
struct TypeTraits<long long>
{
typedef _TrueType _IsPODType; //如果是内置类型则IsPODType是TrueType
};
//11.0
template<>
struct TypeTraits<unsigned long long>
{
typedef _TrueType _IsPODType; //如果是内置类型则IsPODType是TrueType
};
//12.0
template<>
struct TypeTraits<float>
{
typedef _TrueType _IsPODType; //如果是内置类型则IsPODType是TrueType
};
//13.0
template<>
struct TypeTraits<double>
{
typedef _TrueType _IsPODType; //如果是内置类型则IsPODType是TrueType
};
//14.0
template<>
struct TypeTraits<long double>
{
typedef _TrueType _IsPODType; //如果是内置类型则IsPODType是TrueType
};
//使用参数推导的萃取处理
template<class T>
void Copy(const T* src, T* dst, size_t size, _FalseType)
{
cout << "_FalseType:" << typeid(T).name() << endl;
for(size_t i = 0;i < size;++i)
{
dst[i] = src[i];
}
}
template<class T>
void Copy(const T* src, T* dst, size_t size, _TrueType)
{
cout << "_TrueType:" << typeid(T).name() << endl;
memcpy(dst, src, sizeof(T)*size);
}
//使用萃取判断类型的Get函数判断是否是POD类型来处理
template<class T>
void Copy(const T* src, T* dst, size_t size)
{
cout << "_TrueType:" << typeid(T).name() << endl;
//内置类型,使用memcpy或for都可以拷贝
if (TypeTraits<T>::_IsPODType().Get())
{
memcpy(dst, src, size * sizeof(T));
}
else
{
//用户自定义类型,采用for循环拷贝
for (size_t i = 0; i < size; ++i)
{
dst[i] = src[i];
}
}
}
//测试
void Test()
{
string s1[10] = { "1","2","3","4444444444444" };
string s2[10] = { "11","22","33" };
Copy(s1, s2, 10, TypeTraits<string>::_IsPODType());
Copy(s1, s2, 10);
int a1[10] = { 1,2,3 };
int a2[10] = { 0 };
Copy(a1, a2, 10, TypeTraits<int>::_IsPODType());
Copy(a1, a2, 10);
}
int main()
{
Test();
system("pause");
return 0;
}
参考博客:类型萃取