C++ 编译期确定一个类型是不是整型(_Is_any_of_v)

譬如有一个类型T,我们需要知道他是不是整数(int,short,char,long等等),有两种实现方式:

第一种应该是比较容易想到的,利用模板偏特化实现。

template<class T>
struct My_Is_Integer
{
   using type = std::false_type;
};

template<>
struct My_Is_Integer<int>
{
   using type = std::true_type;
};

template<>
struct My_Is_Integer<short>
{
   using type = std::true_type;
};

测试代码如下:

bool bIsInt = My_Is_Integer<int>::type::value; //bIsInt为true

但是这种方式有个缺点,就是如果我们要判断该类型是不是浮点数,又得重新定义结构体,不够灵活。所以考虑能不能利用模板的可变模板参数来实现。这就是第二种方式。

譬如我们先写一个如下的模板类:

template<class T, class... Rest>
struct Is_Include;

这里面T是我们的类型,后面Rest可以写我们期望的类型,只要T是Rest中一个即可。这里的重点就是如何让T与Rest里的参数进行比较。这里需要用到一个可变模板参数展开的技巧。我们知道std::is_same是可以判断两个类型是否相等的,那么我们可以让T与Rest每一个参数构造一个std::is_same<T,Rest>,然后引入另外一个模板类进行承接:

template<class First,class... Rest>
struct Is_Include_1;

template<class T, class... Rest>
struct Is_Include : Is_Include_1<std::is_same<T, Rest>...>{};

这里用到了可变模板参数的展开,利用std::is_same<T,Rest>...将T,Rest...转换为另外一组类型:std::is_same<T,Rest1>, std::is_same<T,Rest2>, std::is_same<T,Rest3>...我们引入另一个类_Is_Include_1来承接这些类型,这些类型有相同的接口和字段,并且存在编译器的字段value, 这个特征很重要,后面需要用。

目前的问题转化为我们有一个模板类Is_Include_1,模板参数类型都是std::is_same,判断这些模板参数类型里有没有一个字段value为true,既然这些模板参数类型都有value字段,那么我们可以把这个参数提取出来作为一个模板参数,因此我们需要引入第三个类Is_include_2, 让Is_Include_1继承Is_Include_2,在继承的过程中将First::value作为Is_include_2的第一个模板参数:

template<bool bSame, class First, class... Rest>
struct Is_Include_2
{
   using type = First;
};

template<class First,class... Rest>
struct Is_Include_1 : Is_Include_2<First::value,First,Rest...>{ };

第三个类的第一个模板参数可以是First:value,这个时候可以引入模板偏特化,对bSame进行偏特化处理:

template<class First, class Next, class... Rest>
struct Is_Include_2<false, First, Next, Rest...> : Is_Include_2<Next::value, Next, Rest...>
{};

如果第一个参数First:value为false,则与偏特化类吻合,然后通过继承来实现可变参数类型的展开,每次展开就是让下一个参数的value也就是Next:value作为第一个参数,Next作为第二个参数,只要Next:value为true,那么就会采用非偏特化的模板类,此时就终止了继承,否则通过继承以相同的方式处理下一个类型,然后我们把第二个参数作为type即可。这样子我们就得到value为true的std::is_same了。

这里面还有个问题需要注意:如果所有的std::is_same的value都是false,那么是不是永远采用偏特化模板来实例化,那么继承不就是陷入死循环了吗。我们可以注意到如果所有的std::is_same的value都是false,那么当继承到只剩下最后一个参数时,Is_Include_2的模板参数类型有两个了,一个是Next::value,一个是Next,后面就没有了。我们注意到偏特化模板类需要至少三个模板参数,所以即便Next::value为false,也会采用非偏特化的模板类实例化,此时type就等于最后一个std::is_same了

最后完整代码如下:

template<bool bSame, class First, class... Rest>
struct Is_Include_2
{
   using type = First;
};

template<class First, class Next, class... Rest>
struct Is_Include_2<false, First, Next, Rest...> : Is_Include_2<Next::value, Next, Rest...>{};

template<class First,class... Rest>
struct Is_Include_1 : Is_Include_2<First::value,First,Rest...>{ };


template<class T, class... Rest>
struct Is_Include : Is_Include_1<std::is_same<T, Rest>...>{};

然后我们再根据需要进行一些处理,使得更好用:

template<class T>
constexpr bool is_int = Is_Include<T,int,short,long>::type::value;//判断是否为整数

template<class T>
constexpr bool is_float = Is_Include<T,float,double>::type::value;//判断是否为浮点数

第二种方式虽然复杂一些,但是后续扩展非常优雅,第二种方式也是C++里_Is_any_of_v的实现思路,算是做了一次源码解读吧。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值