参数推导
考虑如何使用C++实现下面python的代码?即定义一个和变量a类型相同的变量b?
>>> a = int(1)
>>> type(a)
<type 'int'>
>>> b = '2'
>>> type(b)
<type 'str'>
>>> c = type(a)(b)
>>> c
2
>>> type(c)
<type 'int'>
RTTI性质中的typeid()只能够获取类型名称,无法像python中的type()一样用作变量定义。解决办法是使用function template的参数推导机制。
template <class T>
void type_impl(T t) {
T tmp = 1;
cout << tmp << endl; // 1
}
int main() {
int i = 0;
type_impl(i);
}
上述代码的缺点很明显,函数模板无法推导函数返回类型。
内嵌型别
迭代器所指型别称为value_type,如果value_type需要用于函数返回值,参数推导无法实现。解决办法是使用内嵌型别。
template <class T>
struct Iter {
typedef T value_type;
T* ptr;
Iter(T* p = 0):ptr(p) {}
T& operator*() const {
return *ptr;
}
};
template <class I>
typename I::value_type func(I iter) {
// typename通知编译器I::value_type是一个型别
return *iter;
}
int main() {
Iter<int> iter(new int(1));
cout << func(iter) << endl; // 1
}
上述代码也有缺点,比如对于原生指针,是没有value_type内嵌的。
偏特化
偏特化(partial specialization):对于一个以上的模板参数,我们可以针对某个或某些模板参数进行特化工作,也就是将泛化版本中的某些template参数赋予明确的指定。
例如对于:
template <class T>
class C {...}; // 该泛化版本可以允许任意型别
可以有如下偏特化版本。
template <class T>
class C<T*> {...}; // 该特化版本仅允许原生指针型别
有了这个方法,就可以解决原生指针无法内嵌型别的问题。
萃取机
下面的class template专门用来萃取迭代器的特性,value_type正是迭代器的特性之一。
template <class I>
struct iterator_traits {
typedef typename I::value_type value_type;
};
上述代码的意义是:如果I定义有自己value_type,就可以萃取出。
于是我们可以这样使用:
template <class T>
struct Iter {
typedef T value_type;
T* ptr;
Iter(T* p = 0):ptr(p) {}
T& operator*() const {
return *ptr;
}
};
template <class I>
typename iterator_triats<I>::value_type func(I iter) {
return *iter;
}
int main() {
Iter<int> iter(new int(1));
cout << func(iter) << endl; // 1
}
多了这一中间层带来的好处是traits可以拥有特化版本。
template <class T>
struct iterator_traits<T*> {
typedef T value_type;
};
于是即便原生指针没有内嵌型别,也可以通过traits取其value_type。
需要注意的是对于iterator_traits<const int*>::value_type取出的是const int,如果想要取出int,我们需要另一个特化版本。
template <class T>
struct iterator_traits<const T*> {
typedef T value_type;
};
到此为止,无论是迭代器,还是原生指针都可以通过traits取出正确的value_type。
STL约定了5种内嵌型别,我们只有遵守这个约定,才可以兼容STL。
template <class I>
struct iterator_traits {
typedef typename I::iterator_category iterator_category;
typedef typename I::value_type value_type;
typedef typename I::difference_type difference_type;
typedef typename I::pointer pointer;
typedef typename I::reference reference;
};
本文内容参考 —— 《STL源码剖析》