C++ 判断类型的结构体 __are_same

__are_same

C++模板template的存在,为重载函数提供了很多方便。但有些时候我们要对不同的类型作不同的处理(比如整型与浮点型、实数与复数)。比如我要实现如下函数:
f ( x ) = { x + 1 , x 是 i n t 型 , x , x 非 i n t 型 . f(x)= \left\{ \begin{array}{ll} x+1 & ,x是int型, \\ x & ,x非int型. \end{array} \right. f(x)={x+1x,xint,,xint.

那么,很显然我们希望写出这么一个模板函数:

template<typename T>
T f(T x){
    // 如果 T 是 int 型  , 则 return x+1;
    // 否则 return x;
}

问题在于,如何判断 T 是 int 型呢?( T == int 这样的表达式是不合法的)

先来看看解决方案,用到了内置的头文件 <ext/type_traits.h>,以及某个东西:
__are_same<T,int>::__value

#include<iostream>
#include <ext/type_traits.h>
using namespace std;
template<typename T>
T f(T x){
    if (__are_same<T,int>::__value == true){
        return x+1;
    }// else
    return x;
}

int main(){
    cout << "f(5)   = " << f(5)    << endl; // 6
    cout << "f(5.0) = " << f(5.0f) << endl; // 5
    return 0;
}

从效果来看,很容易看懂这个东西判断 T 和 int 是否为同一类型,如果是,则 __value为true,否则 __value 为false.

C++还有类似的其它结构体,比如:
__is_integer<T>(判断是否为整数,如short,int,long,unsigned int…等)
__is_floating<T>(判断是否为浮点数,如float,double,long double等)
__is_pointer<T>(判断是否为指针类型,如int*,char*……等)


如果需求是找到判断类型的方法,那么到此为止这篇文章就已经够用了。如果还想知道其内部是如何实现的,那么请继续往下读。

有默认值的模板结构体

首先轻轻点击这个代码溯源到 __are_same的源代码。在我的电脑上,它位于C:\MinGW\lib\gcc\mingw32\9.2.0\include\c++\bits\cpp_type_traits.h. 这也是我们引用<ext/type_traits.h>的原因。

// Compare for equality of types.
  template<typename, typename>
    struct __are_same
    {
      enum { __value = 0 };
      typedef __false_type __type;
    };

  template<typename _Tp>
    struct __are_same<_Tp, _Tp>
    {
      enum { __value = 1 };
      typedef __true_type __type;
    };

这定义了两段 __are_same,可以看出它们都是结构体。但为什么有两段呢?

解释:
第一段是 __are_same 的默认构造。
第二段是 __are_same 的特别构造。即,如果传入的typename刚好符合第二段,那就用第二段来构造。

在代码上有个区别两者的细节,那就是:第一段 __are_same后面没有尖括号<>,但第二段有

在此处,如果传入 __are_same 的两个类型刚好一样,那就满足第二段的构造,里面的 __value 就是1 (true) ;否则(类型不一样时)默认用第一段构造,__value 值为0.



其实我们也可以自定义一个玩玩。比如一个更实用的例子:实现 f ( x ) f(x) f(x)计算一个复数或实数 x x x的模长 ∣ x ∣ |x| x。要求保持精度。

保持精度,即:如果传入 x x x 是 float 或 complex<float> ,返回值也是 float. 如果传入 x x x 是 double 或 complex<double>,返回值也是 double. 如果传入 x x x 是 long double 或 complex<long double>,则返回值为 long double.


也许你会觉得像下面这样复制六个函数,合计六行就搞定了——那这篇文章就白看了。如果要求的是矩阵范数这样复杂的东西,那么肯定不忍心复制六次。

#include<math.h>
#include<complex>
using namespace std;

float f(float x)                              { return abs(x);}
double f(double x)                            { return abs(x);}
long double f(long double x)                  { return abs(x);}
complex<float> f(complex<float> x)            { return sqrt(real(x)*real(x)+imag(x)*imag(x));}
complex<double> f(complex<double> x)          { return sqrt(real(x)*real(x)+imag(x)*imag(x));}
complex<long double> f(complex<long double> x){ return sqrt(real(x)*real(x)+imag(x)*imag(x));}


我们的应对策略如下:
创建一个 __typeHelper 结构体,其默认值 __is_complex = 0, 类型 __type 为输入的类型。
但是,当输入的东西刚好可以写成 complex<T>的形式,那么就进入第二段的构造,此时complex当中的类型才作为 __type!

#include<iostream>
#include<ext/type_traits.h>
#include<math.h>
#include<complex>
using namespace std;

template<typename T>
struct __typeHelper{
    enum { __is_complex = 0};
    typedef T __type;
};

template<typename T>
struct __typeHelper<complex<T>>{
    enum { __is_complex = 1};
    typedef T __type;
};

//求模长的函数 f
template<class T>
typename __typeHelper<T>::__type f(T x){
    return sqrt(real(x)*real(x)+imag(x)*imag(x));
}

int main(){
    cout << f(complex<double>(3.0,4.0)); // 5
    return 0;
}

最后函数f,输入类型为 T,输出类型是根据结构体 __typeHelper<T>找到的——结构体内的 __type 类型就是输出类型。



有默认值的模板函数

既然存在「有默认值的模板结构体」,那么如法炮制可以写「有默认值的模板函数」。因此上述的求模长的函数也可以这么写:

// 默认的函数形式
template<typename T>
T f(T x){
    return abs(x);
}

// 如果是 complex,则会自动采用如下函数
template<typename T>
T f(complex<T> x){
    return sqrt(real(x)*real(x)+imag(x)*imag(x));
}


而对于第一例, f ( x ) = { x + 1 , x 是 i n t 型 , x , x 非 i n t 型 . f(x)= \left\{ \begin{array}{ll} x+1 & ,x是int型, \\ x & ,x非int型. \end{array} \right. f(x)={x+1x,xint,,xint.
其实现如下:

// 默认的函数形式
template<typename T>
T f(T x){
    return x;
}

// 特别的函数形式
template<>
int f(int x){
    return x+1;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值