类型萃取
类型萃取从字面意思上来说其实就是帮助我们挑选某个对象的类型,筛选特定的对象来做特定的事。
C++中的类型萃取并不是每个人都熟知,他们一般都出现在STL库底层的实现原理中,和笔者一样,相信听到这个名词的读者都好奇类型萃取到底是什么,他是用来做什么事情的?不妨让我们一探究竟
从vector增容说起
要说类型萃取,还需要从vector增容说起,我截取出一段之前笔者模拟实现vector增容的代码:
void Reserve(size_t n) {
if (n > Capicaty()) {
size_t size = Size();
T* tmp = new T[n];
if (size > 0) {
for (size_t i = 0; i < size; i++) {
tmp[i] = this->_start[i];
}
delete[] _start;
}
_start = tmp;
_finish = _start + size;
_endofstorage = _start + n;
}
}
这段代码是完全正确的,但是唯一不足的就是我们拷贝的方式统一使用了赋值的形势。之所以使用“=”赋值的形式,是因为我们在进行深拷贝,防止string类型的对象只拷贝了指针,却没考虑到内存。
这其实就是一个典型的深浅拷贝的问题,我们都知道C++的内置类型变量,例如int、short一类的对象我们只需要进行浅拷贝就可以。除此之外的自定义类型一般都需要进行深拷贝
我们都知道C++是一个极度追求效率的语言,设计者可不甘心就这样对所有类型都一视同仁,所以发明了类型萃取。不难理解:类型萃取帮助我们提取出自定义类型进行深拷贝,而内置类型统一进行浅拷贝,也就是所谓的值拷贝。
模板的特化
现在我们知道了类型萃取实际上是为了帮助我们提取出那些需要进行深拷贝的对象。而如何提取不得不拿出我们的老朋友模板来讲
先抛出一个问题:我现在有一个模板,模板中只有一个变量叫做name,我希望我以int类型实例对象时,name为男孩。当我以float类型实例对象时,我的得到的name为女孩。其他情况统一都为人类
使用模板我们很快的就化解了这个并不复杂的问题:
template<class T>
struct A
{
string name = "人类";
};
template<>
struct A<int>
{
string name = "我是男孩";
};
template<>
struct A<float>
{
string name = "我是女孩";
};
int main()
{
A<int> a;
cout << a.name << endl;//我是男孩
A<float> b;
cout << b.name << endl;//我是女孩
A<string> c;
cout << c.name << endl;//人类
return 0;
}
如果我修改题目:我现在希望所有非内置类型实例出对象时name打印的字符都为人类,内置类型实例出对象时不可以为人类
其实并不难修改,我们只要一一列举所有的内置类型,并为他们生成一份模板的特化即可
使用模板特化如何识别类型
类型萃取的代码并不好懂,我们直接将代码贴过来,对照以上的例子我们就可以很好的理解
struct FalseType
{
bool Get()
{
return false;
}
};
struct TrueType
{
bool Get()
{
return true;
}
};
template<class T>
struct TypeTraits
{
typedef FalseType IsPodType;
};
template<>
struct TypeTraits<int>
{
typedef TrueType IsPodType;
};
...//特化所有内置类型
我们每次都定义TypeTraits对象,如果是内置类型时就一定都将触发特化的模板,我们每次都取IsPodType,如果是内置类型,我们就将取到TrueType ,相反取到FalseType
使用TrueType 和FalseType 也就可以定义他们的匿名对象,并且可以调用Get方法,内置类型则返回true,反正为false
举个例子:假设T为int类型,那么取到的IsPodType则是TrueType ,使用IsPodType().Get()则返回的结果是true,如果是自定义类型则返回了false
TypeTraits<T>::IsPodType().Get();
使用类型萃取改进vector扩容
现在我们使用类型萃取就可以进行vector增容的类型区分了
struct FalseType
{
bool Get()
{
return false;
}
};
struct TrueType
{
bool Get()
{
return true;
}
};
template<class T>
struct TypeTraits
{
typedef FalseType IsPodType;
};
template<>
struct TypeTraits<int>
{
typedef TrueType IsPodType;
};
...//特化所有内置类型
void Reserve(size_t n)
{
if (n > Capacity())
{
size_t size = Size();
T* tmp = new T[n];
if (size > 0)
{
if (TypeTraits<T>::IsPodType().Get())
{
memcpy(this->_start, tmp, sizeof(T)*size);
}
else
{
for (int i = 0; i < size; i++)
{
tmp[i] = _start[i];
}
}
delete[] _start;
}
_start = tmp;
_finish = _start + size;
_endofstorage = _start + n;
}
}