首页 > 代码库 > 【C++11学习笔记】类型判断的type_traits学习

【C++11学习笔记】类型判断的type_traits学习

一、简单的type_traits

我理解的type_traits是利用C++模板特性和static、enum特性定义编译器常量,例如

//std::integral_constant源码
typelate<class T, T v>
struct integral_constant
{
    static const T value = http://www.mamicode.com/v;"hljs-keyword">typedef T value_type;
    typedef integral_constant<T, v> type;
    operator value_type() {return value;}
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这里利用的是static常量为编译器常量的特,定义了value。使用方法:从std::integral_constant派生,无需自己定义static const常量或enum类型,例如

template<typename T>
struct GetSize : std::integral_constant<int, 1>
{
};
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

std有两个定义好的std::integral_constant实例,分别定义了编译期的true和false类型,用途很广:

typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;
  • 1
  • 2
  • 1
  • 2

二、常见类型判断type_traits源码学习

1.is_void

声明:

template<class T>
struct is_void;
  • 1
  • 2
  • 1
  • 2

作用:

T是否为void类型

源码:

template<class T, class U>
struct is_same : std::false_type
{};

template<class T>
struct is_same : std::true_type
{};

template<class T>
struct is_void : std::is_same<void, typename std::remove_cv<T>::type>
{};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

说明:首先利用模板的匹配实现用以判断两种类型是否一致的is_name,再将T去除c(const)、v(volatile)限定符后与void类型判断是否一致。下面有些简单的代码就不解释了。

2.is_floating_point

声明

template< class T >
struct is_floating_point;
  • 1
  • 2
  • 1
  • 2

作用

T是否为浮点类型

源码

template< class T >
struct is_floating_point : std::integral_constant<bool,std::is_same<float, typename std::remove_cv<T>::type>::value || std::is_same<double, typename std::remove_cv<T>::type>::value || std::is_same<long double, typename std::remove_cv<T>::type>::value> 
{};
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

3.is_array

声明

template<class T>
struct is_array;
  • 1
  • 2
  • 1
  • 2

作用

T是否为数组类型

源码

template<class T>
struct is_array : std::false_type {};

template<class T>
struct is_array<T[]> : std::true_type {};

template<class T, std::size_t N>
struct is_array<T[N]> : std::true_type {};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4.is_pointer

声明

template< class T > 
struct is_pointer;
  • 1
  • 2
  • 1
  • 2

作用

T是否为指针类型(包括函数指针,但不包括成员(函数)指针) 
源码

template< class T > struct is_pointer_helper     : std::false_type {};
template< class T > struct is_pointer_helper<T*> : std::true_type {};
template< class T > struct is_pointer : is_pointer_helper<typename std::remove_cv<T>::type> {};
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

5.is_member_pointer

声明

template< class T >
struct is_member_pointer
  • 1
  • 2
  • 1
  • 2

作用

T是否为成员函数指针、指向成员变量指针类型 
源码

template< class T >
struct is_member_pointer_helper : std::false_type {};

template< class T, class U >
struct is_member_pointer_helper<T U::*> : std::true_type {};

template< class T >
struct is_member_pointer : is_member_pointer_helper<typename std::remove_cv<T>::type> 
{};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

为什么is_member_pointer_helper< T U::*>这个就是成员函数指针、指向成员变量指针类型呢?

可以见我另外一篇文章:C++如何声明类成员函数指针或类成员变量指针(A::*)。 
这个参数T U::*怎么理解,其实就理解成T *——T类型指针,但是是类U中的,即类U的成员函数指针或成员变量指针,看下面的测试代码:

#include <iostream>
#include <type_traits>

int main() {
    class cls {};
    std::cout << (std::is_member_pointer<int(cls::*)>::value
                     ? "T is member pointer"
                     : "T is not a member pointer") << ‘\n‘;
    std::cout << (std::is_member_pointer<int cls::*>::value
                     ? "T is member pointer"
                     : "T is not a member pointer") << ‘\n‘;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

输出是

T is member pointer
T is member pointer
  • 1
  • 2
  • 1
  • 2

注意,并不是判断类T中是否真的有返回值为int的函数,或者是否有int型变量,而是只是判断T这个写法是否是成员函数指针、指向成员变量指针类型。

6.is_class

声明:

template <class T>
struct is_class;
  • 1
  • 2
  • 1
  • 2

作用

T是否为类类型,且不是union类型 
源码

namespace detail {
    template <class T> char test(int T::*);
    struct two { char c[2]; };
    template <class T> two test(...);
}

template <class T>
struct is_class : std::integral_constant<bool, sizeof(detail::test<T>(0))==1 && !std::is_union<T>::value> 
{};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

解释一下,定义了两个模板函数,一个形参是int T::*(指向int型类成员变量的指针),返回值是char(大小是1);另一个形参是所有类型,返回值是struct two(大小是2)。

is_class继承了std::integral_constant< T, T v >(内部定义了一个static const T类型变量value,取值为v),value的类型为bool,当detail::test(0)的大小为1时(只要T是class类型,就符合第一个模板函数test,则其返回值大小就为1,否则返回值大小为2),并且不为union类型时(加上这个是因为,union类型类似struct类型,也支持T::*),则为class(或struct)类型。

7.is_base_of

声明

template <typename Base, typename Derived>
class is_base_of;
  • 1
  • 2
  • 1
  • 2

作用

Base是否是Derived的基类

源码

template <typename Base, typename Derived, 
    bool = (is_class<Base>::value && is_class<Derived>::value)>
class is_base_of
{
    template <typename T>
    static char helper(Derived, T);
    static int helper(Base, int);
    struct Conv
    {
        operator Derived();
        operator Base() const;
    };
public:
    static const bool value = http://www.mamicode.com/sizeof(helper(Conv(), 0)) == 1;
};


template <typename Base, typename Derived>
class is_base_of<Base, Derived, false>
{
public:
    static const bool value = http://www.mamicode.com/is_same::value;"hljs-keyword">class B
{
};

class D : public B
{
};

int main()
{
    cout << boolalpha << is_base_of<B, D>::value << endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

代码中最“厉害”的地方就是对helper函数的匹配了。

  1. 如果Base不是Derived的基类,那么Conv()做隐式转换时,两个候选类型Base和Derived都是平等的,两个helper函数都可以匹配,但在这里按照规则,会去优先匹配非模板的函数。于是得到了我们想要的结果。

  2. 如果Base是Derived的基类,这种情况比较复杂。

这种情况下,除非Conv对象是一个const的,否则它的隐式转换是只会去调用operator ()Derived的,因为operator ()Base const后面所带的const。于是这样的情况下,Conv()总是隐式转换成一个Derived对象(当然,上面的只是学习代码,如果真正要实用的话,还要做很多工作,比如说在这里就要首先确保Derived类型本身不是const的),这时候对于两个helper的第一个参数,一个是精确匹配,一个是要转换为基类,一开始我异想天开地以为这种情况下就会去先匹配第一个了,因为这也是所需要的正确结果,结果自然是没有错,不过我的想法却是太天真了,因为我完全抹杀了第二个参数的贡献,倘若没有它,那第一个helper函数都不会被具现化,更别说让它去被匹配了。

【参考: 
1.《深入应用C++11代码优化与工程级应用》 
2.http://en.cppreference.com/w/ 
3.TR1中is_base_of的实现】

【C++11学习笔记】类型判断的type_traits学习