大忙人系列-程序猿在想什么 (四·二) C++的食用方法
三. 模板是如何整活的
从一个简单的iterSwap开始,假设能交换两个迭代器指向的元素。
template<typename Iter1,typename Iter2>
void iterSwap(Iter1 iter1,Iter2 iter2) {
T temp = *iter1;
*iter1 = *iter2;
*iter2 = temp;
}
那么问题来了,假设你刚好忘记有个东西叫auto,又忘记了有个东西叫decltype,这个时候你的T应该从哪里来呢?明明是一个编译时期肯定能确定的类型,但是却不知从何获取。于是,会整活的人奇思妙想硬是整了出来:
T temp = *iter1; 改为:
typename Iter1::valueType temp = *iter1;
这样只需要在每个Iter类里面typedef XXX valueType; 就可以了。
那么问题又来了,有一天一个傻子往这个函数里面丢了两个指针,然后看着哗哗报错的模板。明明指针和迭代器的行为相似,那怎么同时实现两者呢?于是,聪明的猿人想出了方法把“指针”这个“特性”“萃取”出来,也就是traits。
template<typename Iter>
struct IterTraits {
typedef typename Iter::valueType valueType;
};
template<typename T>
struct IterTraits<T*> {
typedef T valueType;
};
之前的代码改为:
typename IterTraits<Iter1>::valueType temp = *iter1;
就可以了。
显然,这里就肯定涉及到了SFINAE(Substition Failure Is Not An Error),不能说在尝试实例化IterTraits的时候,指针尝试选择第一个版本结果出错了,就直接视为错误而停止编译;而是要再接着尝试下面这个版本,发现可以匹配,于是通过。
于是,一个简单的iterSwap实现完毕(例子仅用于示意,实战这么写依然是要被人打死的请注意)
#include <type_traits>
这个头文件存了大量的比较实用的模板,平时想骚一把的可以瞅瞅。
std::is_XXX系列模板,比如is_array,一个可能的实现方法:
template<typename T>
struct is_array : std::false_type {};
template<typename T>
struct is_array<T[]> : std::true_type {};
template<typename T, std::size_t N>
struct is_array<T[N]> : std::true_type {};
然后是std::conditional,可能的实现有:
template<bool B, typename T, typename F>
struct conditional { typedef T type; };
template<typename T, typename F>
struct conditional<false, T, F> { typedef F type; };
e.g.
typedef std::conditional<USEINT,int,float>::type Type;
接着是std::enable_if,可能的实现有:
template<bool B, class T = void>
struct enable_if {};
template<class T>
struct enable_if<true, T> { typedef T type; };
就是在条件下可以有类似屏蔽类型的效果,可以导致出错然后SFINAE。

