type_traits之has_* 系列

来源:互联网 发布:soa java 编辑:程序博客网 时间:2024/05/17 22:27
     有时候我们需要判断两个类型是否支持某个运算操作符,比如+/-等等。C++里该如何在编译期判定类里面支持此类需求呢?本文基于Boost库源码,尝试作一番探讨。
不妨先定义一个类:
template<class Lhs,class Rhs>
strcut has_plus
{
    static const bool value = false;
}

这个判定类直接写死,永远是false,即不支持+操作符
如果我们传入两个int类型,语言内置支持 '+':
bool bhasplus = has_plus<int,int>::value;// 永远等于false,但是应该为true
我们现在要做的就是扩充has_plus,使得能判断各种类型是否支持。主要是分两步走,第一是先支持内部类型,其次支持自定义类型。
实际上C++语言内置的很多类型都支持+,但也有一些是不支持的,最简单的方法就是遍历的形式,直接通过模板偏特化技术把它挑选出来或者过滤掉。

template<class Lhs,class Rhs>
struct has_plus<int,int>
{
    static const bool value = true;
}
但是,这样还要写各种类型(bool,char double...),以及正交操作,很是麻烦,代码量也大
所以我们可以写一个过滤器, 先定义:“或”, “与”,“非”
template <bool b1, bool b2 , bool b3 = false , bool b4 = false , bool b5 = false , bool b6 = false , bool b7 = false >
struct   type_or;

template <bool b1, bool b2 , bool b3 , bool b4, bool b5 , bool b6 , bool b7>
struct   type_or
{
    static const bool value = true;
};

template <>
struct   type_or< false , false , false , false , false , false , false >
{
     static const bool value = false;
};

template <bool b1, bool b2 , bool b3 = true , bool b4 = true , bool b5 = true , bool b6 = true, bool b7 = true>
struct   type_and;

template <bool b1, bool b2 , bool b3 , bool b4, bool b5 , bool b6 , bool b7>
struct   type_and
{
    static const bool value = false ;
};

template <>
struct   type_and< true , true , true , true , true , true , true >
{
     static const bool value = true ;
};

template <bool b>
struct type_not
{
  static const bool value = true;
};

template <>
struct type_not< true >
{
  static const bool value = false ;
};

type_or 表示只有所有类型参数都是false,value才是false,type_and 则所有都是true,value才是true. type_not 参数是false,则value是true.

下一步就是归类
template <class Lhs_noptr, class Rhs_noref >
struct Forbidden_if
{
  static const bool value = type_or <
  type_and < is_pointer < Lhs_noptr >:: value, is_pointer < Rhs_noref >:: value>:: value ,//Lhs==pointer and Rhs==pointer
  type_and < is_pointer < Lhs_noptr >:: value, is_void < Lhs_noptr >:: value, is_fundamental < Rhs_noref >:: value>:: value ,//Lhs==void* and Rhs==fundamental
  type_and < is_pointer < Rhs_noref >:: value, is_void < Rhs_noref >:: value, is_fundamental < Lhs_noptr >:: value>:: value ,//Rhs==void* and Lhs==fundamental
  type_and < is_pointer < Lhs_noptr >:: value, is_fundamental < Rhs_noref >:: value, type_not< is_integral < Rhs_noref >:: value>:: value >::value , //Lhs==pointer and Rhs==fundamental and Rhs!=integral
  type_and < is_pointer < Rhs_noref >:: value, is_fundamental < Lhs_noptr >:: value, type_not< is_integral < Lhs_noptr >:: value>:: value >::value //Lhs==pointer and Rhs==fundamental and Rhs!=integral
  > :: value ;
};

这样所有内置类型就全部支持了。让我们重新修改下has_plus的实现
template<class Lhs,class Rhs,bool Forbidden_if >
struct has_plus;

template<class Lhs,class Rhs>
struct has_plus<Lhs,Rhs,true>
{
    static const bool value = false;
}

template<class Lhs,class Rhs>
struct has_plus<Lhs,Rhs,false>
{
    static const bool value = true;
};
让我们调用下试试:
bool bhasplus = has_plus<int,int,Forbidden_if<int,int>::value >::value;// 对于内置类型,如果支持,就显示为true;

对内置类型的判断就算完成了。
下面看如何对自定义类型进行判断。
平时写代码的时候,一个简单的常用的方法是直接写一行代码T1()+T2()看看是否编译通过,通过说明支持,否则不支持。但是模板编程不能这样子,编译器报错说明代码有问题。那我们要怎么屏蔽掉这个错误呢?还是需要借助于模板偏特化技术。
我们可以自定义一个+运算操作函数。
定义一个函数,用来生成T实例,这里不需要实现,只是用来做类型推倒,自然也不要求提供默认构造函数
template <typename T> T & make();
这里再定义一个类型标识符
struct no_operator { };
struct has_operator { };//与no_operator作为参照。
这里定义一个可以接收任意类型的封装类。
struct any { template <class T> any( T const &); };
于是一个+重载函数就出炉了。这个函数提供了默认的+语义,如果T类型没有自己的运算符,就会调用这个,并且返回no_operator
no_operator operator +( const any &, const any&);

这样一来,我们就只需要执行以下代码 make<Lhs>()+ make<Rhs>()  如果返回类型是no_operator,则表示没有内置+运算操作符。那么我们怎么判断是no_operator呢?
可以写两个函数:
template<class T> int s_check(T);
template<> char  s_check<no_operator >(no_operator );
这样我们调用s_check(make< Lhs >() +make < Rhs>()));就可以用sizeof判定其返回类型大小了。
然而,很遗憾,s_check函数偏特化是不合法的。。。只能改成以下写法
chars_check(has_operator);
int  s_check(no_operator );
可是我们T类型明显不是这个,怎么让他接受呢?
boost开发人员通过重定义操作符“,”来实现这个功能。
no_operator operator,( no_operator , has_operator );
如果表达式(make<Lhs>()+ make<Rhs>(), make < has_operator>())的前半部分返回的是no_operator,则完全匹配到我们的自定义类型里,否则就走常规的","的定义,即返回make < has_operator>()的类型,即has_operator。
这样一来,s_check()函数就自动匹配,返回char或者int,再调用sizeof判断大小即可。
template < typename Lhs , typename Rhs >
struct operator_exists {
   static const bool value = ( sizeof (s_check ( ( make< Lhs >() +make < Rhs>()),make < has_operator>() ) == sizeof(char));
};

template<class Lhs,class Rhs>
struct has_plus<Lhs,Rhs,false>
{
    static const bool value = operator_exists<Lhs,Rhs>::value;
}
到这里,我们算完成了+操作符定义的判断。主要的技术已经讲完,但是实际测试的时候会发现,void类型是一个特例。所以需要排除掉。
template<class Lhs,class Rhs>
struct has_plus<void,Rhs,false>
{
    static const bool value = false;
}

template<class Lhs,class void>
struct has_plus<void,Rhs,false>
{
    static const bool value = false;
}

template<class Lhs,class Rhs>
struct has_plus<void,void,false>
{
    static const bool value = false;
}

完整代码实现如下:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <bool b1, bool b2 , bool b3 = false , bool b4 = false , bool b5 = false , bool b6 = false , bool b7 = false >
struct    type_or;

template <bool b1, bool b2 , bool b3 , bool b4, bool b5 , bool b6 , bool b7>
struct   type_or
{
    static const bool value = true;
};

template <>
struct    type_or < false , false , false , false , false , false , false >
{
    static const bool value = false;
};

template <bool b1, bool b2 , bool b3 = true , bool b4 = true , bool b5 = true , bool b6 = true, bool b7 = true>
struct   type_and;

template <bool b1, bool b2 , bool b3 , bool b4, bool b5 , bool b6 , bool b7>
struct    type_and
{
    static const bool value = false;
};

template <>
struct type_and < true , true , true , true , true , true , true >
{
    static const bool value = true;
};

template <bool b>
struct type_not
{
    static const   bool value = true;
};

template <>
struct type_not < true >
{
    static const bool value = false;
};

template <class Lhs_noptr, class Rhs_noref >
struct Forbidden_if
{
    static const bool value = type_or <
        type_and < is_pointer < Lhs_noptr >:: value, is_pointer < Rhs_noref >:: value>:: value ,//Lhs==pointer and Rhs==pointer
        type_and < is_pointer < Lhs_noptr >:: value, is_void < Lhs_noptr >:: value, is_fundamental < Rhs_noref >:: value>:: value ,//Lhs==void* and Rhs==fundamental
        type_and < is_pointer < Rhs_noref >:: value, is_void < Rhs_noref >:: value, is_fundamental < Lhs_noptr >:: value>:: value ,//Rhs==void* and Lhs==fundamental
        type_and < is_pointer < Lhs_noptr >:: value, is_fundamental < Rhs_noref >:: value, type_not< is_integral < Rhs_noref >:: value>:: value >::value , //Lhs==pointer and Rhs==fundamental and Rhs!=integral
        type_and < is_pointer < Rhs_noref >:: value, is_fundamental < Lhs_noptr >:: value, type_not< is_integral < Lhs_noptr >:: value>:: value >::value //Lhs==pointer and Rhs==fundamental and Rhs!=integral
    > :: value ;
};

template < class Lhs , class Rhs , bool Forbidden_if >
struct has_plus_base;

template < class Lhs , class Rhs >
struct has_plus_base < Lhs , Rhs , true >
{
    static const bool value = false;
};

struct no_operator { };
struct has_operator { }; //与no_operator作为参照。
no_operator operator,( no_operator, has_operator );
template <typename T> T& make();
char s_check( has_operator );
int   s_check( no_operator );

template < typename Lhs , typename Rhs >
struct operator_exists {
    static const bool value =   sizeof( s_check( (( make < Lhs >() + make < Rhs>() ), make < has_operator >()) ) == sizeof ( char ) );
};

template < class Lhs , class Rhs >
struct has_plus_base < Lhs , Rhs , false >
{
    static const bool value = operator_exists< Lhs , Rhs >::value ;
};

template < class Rhs>
struct has_plus_base < void , Rhs , false >
{
    static const bool value = false;
};

template < class Lhs >
struct has_plus_base < Lhs , void, false >
{
    static const bool value = false;
};

template <>
struct has_plus_base < void , void , false >
{
    static const bool value = false;
};

template < class Lhs ,class Rhs>
struct has_plus: has_plus_base <Lhs , Rhs, Forbidden_if <Lhs , Rhs>:: value >
{
};

void test()
{
  bool b =has_plus_base < void, int , Forbidden_if < int, int >::value >:: value ;
  cout << b << endl; // false
}
///////////////////////////////////////////////////////////////////////////////////////////
0 0
原创粉丝点击