我们在设计一些模板类或者模板函数的时候,通常会提供一个typename T作为通用参数,假如我们要设计FArgument
template<typename T>
struct FArgument
{
T value;
}
现在这个FArgument,是可以接受任何类型作为模板参数的,但如果我们有需求,只想限定:int bool double作为参数,该怎么做?
模板特化行不行?
不行!模板特化只是针对某些模板参数的特殊处理,它起不到禁止参数的作用。
这个时候,就可以使用traits技法来达到目的。使用方式是在模板类中引入一个额外的类型参数,这个类型参数只有在T满足某些条件的时候才被定义。
template<typename T>
struct CheckArgument
{
};
template<>
struct CheckArgument<int>
{
using type = int;
};
template<typename T, typename U = CheckArgument<T>::type >
struct FArgument
{
T value;
};
FArgument<int> a; //ok
FArgument<float> a; //error: "type": 不是 "CheckArgument<T>" 的成员
FArument的定义中,U没有任何实际作用,只用来对T进行类型萃取,即Traits,如果我们能从T中Traits出type,说明T是满足要求的,否则就不满足要求,编译失败。
而这,不正是我们开始的需求吗?Good!
其实现在这种做法就已经能够满足我们的需求了,而实际上,对于这种需求,当前已经有通用的解决模型,STL提供了一个模板类enable_if,用来萃取类型
template <bool _Test, class _Ty = void>
struct enable_if {}; // no member "type" when !_Test
template <class _Ty>
struct enable_if<true, _Ty> { // type is _Ty for _Test
using type = _Ty;
};
而我们要做的,从萃取出type变为萃取出true或者false,然后传给enable_if,最后使用它的type,看这个type是否能被萃取到,即变为如下形式
template<typename T>
struct CheckArgument
{
};
template<>
struct CheckArgument<int>
{
enum {value = true};
};
template<typename T, typename U = typename enable_if<CheckArgument<T>::value>::type >
struct FArgument
{
T value;
};
FArgument<int> a; //ok
FArgument<float> a; //error: “value”: 未声明的标识符
因为这个typename U没有被使用,所以它甚至可以没有名字!即所谓的匿名类型参数。即变为下面这种代码
template<typename T>
struct CheckArgument
{
};
template<>
struct CheckArgument<int>
{
enum {value = true};
};
//注意!!!!下面的U被我们删除了!
template<typename T, typename = typename enable_if<CheckArgument<T>::value>::type >
struct FArgument
{
T value;
};
FArgument<int> a; //ok
FArgument<float> a; //error: “value”: 未声明的标识符
没有了U,也不影响任何代码, 而且我们也不用担心这个U被误使用了,not bad!
以后我们看到这匿名类型参数,就知道,又要对T进行类型限制了,怎么限制?enable_if内的代码是核心!
引入了enable_if之后,使得模板代码变得复杂了很多,第一眼看,容易被唬住,但经过我们的分析,它也很简单,就是通过传入的true false萃取类型。
所以,如果之后需要对模板类型进行选择过滤的话,你应该知道怎么做了吧?:D