1.10 type traits

/// integral_constant
template<typename _Tp, _Tp __v>
struct integral_constant {
static constexpr _Tp value = __v; // 注意static
typedef _Tp value_type;
typedef integral_constant<_Tp, __v> type;
constexpr operator value_type() const { return value; }
constexpr value_type operator()() const { return value; }
};
// 类静态成员的再次声明,在c++11中,constexpr应该不是需要重新声明定义的。
template<typename _Tp, _Tp __v>
constexpr _Tp integral_constant<_Tp, __v>::value;

不一样的operator

#include <iostream>
class A {
public:
operator int() const {
std::cout << "value_type()" << std::endl;
return 100;
}
int operator()() const {
std::cout << "operator()()" << std::endl;
return 101;
}
};
int main(void) {
A a;
int t = a; // 这里调用operator int() const
a(); // 这里是做为仿函数被调用
return 0;
}

所以这里需要区别operator type() consttype operator() const
一个是用来进行类型转换,一个是用来模拟仿函数。

integral_constant 的作用

字面常量参数

在模板编程中,如果一个模板参数不是typename T而是int N或者bool cond这种参数,那么这个参数必须是字面常量。

template<bool cond, typename T>
class A ...
// 使用时
A<true, int> aInt;
A<false, int> bInt;

而不能用

bool cond = false;
A<cond, int> aInt; // 这里编译出错

当然也可以使用c++11引入的字面常量表达式

constexpr bool cond = true;
A<cond, int> aInt; // OK

总结
使用模板的时候,编译器的输入就是文本里面的字符,是不能存在任何存储空间的变量。因此,模板参数只能使用类型或者是字面常量。

也就是说,模板元编程的输入有两种:

  • 类型,比如int, double, float, 以及自定义类型。
  • 类型常量,比如自定义了某种类型,然后定义了这种类型的字面常量。或者内建类型的字段常量,比如false/true或者constexpr bool cond = true这里cond也是属生字面常量。

如何通用地定义字面常量

由前面的模板元输入的可以知道,类型有两种,一种是类型一种是类型对应的常量。那么是否有方法可以把两者统一起来?

首先看一下支持类型常量的写法。

template<typename T, bool X>
SomeCode
// use
SomeCode<someType, true> x;

在第二个参数这里就必须显示地指定类型。那么如何后面要支持其他通用的类型的常量,比如int类型的常量,又需要把同样的代码抄一遍。

temlate<typename T, int X>
SomeCode
// use
SomeCode<someType, 10> x;

这样就显示得一点也不泛型了。那么最好的办法是利用一个抽象把类型字面常量表示起来。这就是integral_constant

首先看一下如何定义bool类型的常量:

typedef std::integral_constant<bool, true> true_type
typedef std::integral_constant<bool, false> false_type

那么,后面在定义模板的时候,就可以只写一份了。为了区分,这里把SomeCode改成SomeClass

template<typename T, typename ConstValue>
class SomeClass {
// 引用到给定的常量的类型
typedef typename ConstValue::type const_type;
// 引用到给定的常量的值
constexpr const_type value = ConstValue::value;
};

但是使用方式上就会有点麻烦了。

// 原来是SomeCode<int, false> aInst;
// 现在是
typedef std::integral_constant<bool, true> true_type
typedef std::integral_constant<bool, false> false_type
SomeClass<int, false_type> aInst;
// 原来是SomeCode<int, 10> bInst;
// 现在是如下
SomeClass<int, std::integral_constant<int,10>> bInst;

总结

  • 从抽象上来说,代码的实现方直接把类型字面常量抽象成一个模板,并且type/value可以引用到相应的类型和值。
  • 服务方SomeClass里面在引用到类型和字面常量值的时候,就通过::type/::value来引用。
  • 客户端原本使用SomeCode<int, 10>的地方要修改成SomeClass<int, std::integral_constant<int,10>>

定义一些常量

// The type used as a compile-time boolean with true value.
typedef integral_constant<bool, true> true_type;
// The type used as a compile-time boolean with false value.
typedef integral_constant<bool, false> false_type;
template<bool __v>
using __bool_constant = integral_constant<bool, __v>;
template<bool __v>
using bool_constant = integral_constant<bool, __v>;

if/else

// Primary template.
/// Define a member typedef @c type to one of two argument types.
template<bool _Cond, typename _Iftrue, typename _Iffalse>
struct conditional
{ typedef _Iftrue type; };
// Partial specialization for false.
template<typename _Iftrue, typename _Iffalse>
struct conditional<false, _Iftrue, _Iffalse>
{ typedef _Iffalse type; };

这个和前面自己实现的if/else模板元本质上没有太大区别。

template<bool cond, typename If, typename Then>
struct IF {
typedef If type;
};
template<typename If, typename Then>
struct IF<false, If, Then> {
typedef Then type;
};

除了命名不一样,代码本质是一样的。

可变参数

c++11开始支持模板可变参数,使用方式如下:

#include <iostream>
//前向声明
template<typename... Args>
struct Sum;
//基本定义
template<typename First, typename... Rest>
struct Sum<First, Rest...> {
static constexpr int value = Sum<First>::value + Sum<Rest...>::value;
};
//递归终止
template<typename Last>
struct Sum<Last> {
static constexpr int value = sizeof (Last);
};
int main(void) {
std::cout << Sum<float, int, double>::value << std::endl;
return 0;
}

类型的or操作

有了这个小程序,那么再看接下来的一个stl里面的模板就容易了。

template<typename...>
struct __or_;
// 当没有给定参数的时候,返回false_type
// 本质上就是一个false_type
template<>
struct __or_<>
: public false_type
{ };
// 如果只有一个参数B1, 那么返回值就是
// B1
template<typename _B1>
struct __or_<_B1>
: public _B1
{ };
// 如果有两个参数的时候,那么就根据第一个参数的
// 值来决定返回的类型
template<typename _B1, typename _B2>
struct __or_<_B1, _B2>
: public conditional<_B1::value, _B1, _B2>::type
{ };
// 实际上等价的效果就是
// B1::value | B2::value .. | Bn::value
template<typename _B1, typename _B2, typename _B3, typename... _Bn>
struct __or_<_B1, _B2, _B3, _Bn...>
: public conditional<_B1::value, _B1, __or_<_B2, _B3, _Bn...>>::type
{ };

这个__or_的作用就是取出所有给定类型的第一个true/非零类型

#include <iostream>
#include <type_traits>
int main(void) {
typedef std::integral_constant<int, 10> int_10_cont;
auto x = std::__or_<int_10_cont, std::true_type,std::false_type,std::true_type>::value;
std::cout << x << std::endl;
return 0;
}

总结

关于or类型操作的总结就是,符合C/C++里面的a||b操作,如果a非0,那么就可以直接返回a了。

类型的and操作

了解了前面的if/or操作,可以利用conditional来操作了。只不过需要注意的是conditional的使用。在a||b的时候,使用是

if (A::value) {
return A::type;
} else {
return B::type;
}

那么and操作的时候就需要这样。

if (A::value) {
return B::type;
} else {
return A::type;
}

但是正式的写法是conditional<A::value, B::type, A::type>

所以原本一个非常简单的类型操作函数,需要写成如下格式。

template<typename...>
struct __and_;
// 注意空的时候,返回true_type
template<>
struct __and_<>
: public true_type
{ };
// 只有一个类型的时候,直接返回这个类型
template<typename _B1>
struct __and_<_B1>
: public _B1
{ };
// 如同前面说的if的结构
template<typename _B1, typename _B2>
struct __and_<_B1, _B2>
: public conditional<_B1::value, _B2, _B1>::type
{ };
template<typename _B1, typename _B2, typename _B3, typename... _Bn>
struct __and_<_B1, _B2, _B3, _Bn...>
: public conditional<_B1::value, __and_<_B2, _B3, _Bn...>, _B1>::type
{ };

这里需要特别注意一下。后面这段语句类似于下面这个函数。

type and(B1, B2, B3, ... Bn) {
if (B1::value) {
return and(B2, B3, Bn...);
} else {
return B1::type;
}
}

not类型

对于一个字面常量类型也可以进行not操作。

template<typename _Pp>
struct __not_
: public integral_constant<bool, !_Pp::value>
{ };

可以发现,这里通过!_Pp::value直接转换成了

typedef std::integral_constant<bool, true> true_type
typedef std::integral_constant<bool, false> false_type

交并补

这里针对类型的列表有如下操作交/并/补,只不过这里补操作只针对有一个类型。

template<typename... _Bn>
struct conjunction
: __and_<_Bn...>
{ };
// 注意,... _Bn可变参数列表
// 都是有::value类型的,
// 后面声明了inline变量方便引用
template<typename... _Bn>
struct disjunction
: __or_<_Bn...>
{ };
template<typename _Pp>
struct negation
: __not_<_Pp>
{ };

由于每个操作,都会返回相应的integral_constant类型,所以操作的结果是都会有::value成员。为了方便引用这些成员,又定义了别名。

template<typename... _Bn>
inline constexpr bool conjunction_v
= conjunction<_Bn...>::value;
template<typename... _Bn>
inline constexpr bool disjunction_v
= disjunction<_Bn...>::value;
template<typename _Pp>
inline constexpr bool negation_v
= negation<_Pp>::value;

是否有参数

有时候,需要判断是否有参数。那么写法如下:

template<typename _Tp>
struct __success_type
{ typedef _Tp type; };
struct __failure_type
{ };

这里定义的两个类型,需要重点看一下相应的这段注释。

// For several sfinae-friendly trait implementations we transport both the
// result information (as the member type) and the failure information (no
// member type). This is very similar to std::enable_if, but we cannot use
// them, because we need to derive from them as an implementation detail.
// 对于某些sfinae友好的trait函数而言,返回值有两种,一种是需要返回相应的类型(毕竟是模板元编程,函数的输入与输出都是类型),另外一种返回值是没有任何类型输出,这个时候就用`__failure_type`,这个与std::enable_if是相似的,但是不能用`enable_if`,因为需要得到实施的具体细节。

is_void

有时候我们想知道一个类型是否是void类型。那么如何判断?这个时候就需要利用偏特化。首先看一下客户端代码是如何使用这个is_void的。

#include <iostream>
#include <type_traits>
int main(void) {
std::cout << std::is_void<void>::value << std::endl;
std::cout << std::is_void<int>::value << std::endl;
return 0;
}

那么is_void是如何实现的呢?一种比较简单的实现可能如下:

template<typename T>
struct is_void {
static constexpr bool value = false;
};
template<>
struct is_void {
static constexpr bool value = true;
};

但是,在之前的type_traits里面是定义了各种信息丰富的常量的。这里可以借助于true_type/false_type来定义。

template<typename>
struct remove_cv;
template<typename>
struct __is_void_helper
: public false_type { };
template<>
struct __is_void_helper<void>
: public true_type { };
/// is_void
template<typename _Tp>
struct is_void
: public __is_void_helper<typename remove_cv<_Tp>::type>::type
{ };

当然,这里的实现还是比较细致的,还去除了const void/volatile void等修饰符之后再判断。

如果写成一个函数就是

bool_type __is_void_helper(type) {
if (type == void) {
return true_type;
} else {
return false_type;
}
}
true_type/false_type is_void(input_type) {
temp_type = remove_cv(input_type);
return __is_void_helper(temp_type);
}

关于继承

这里需要说明,一种简单的实现是没有必要采用继承来处理的。比如

template<typename T>
struct is_void {
static constexpr bool value = false;
};
template<>
struct is_void {
static constexpr bool value = true;
};

但是如果其他类型,比如is_int/is_float等等这样的函数都需要这么定义,就会显得特别的繁琐。每个都需要在里面进行相同的定义,那么一种简单的办法当然是采用C++的继承了。

template<typename T>
struct is_void : public false_type {
};
template<>
struct is_void : public true_type {
};

但是,这里还需要考虑的是,如果有const void/volatile void应该怎么办?这个时候,就需要先通过remove_cv把类型里面的const/volatile去掉。所以这里还需要中间加一层。

template<typename T>
struct __is_void_helper : public false_type {
};
template<>
struct __is_void_helper : public true_type {
};
template<typename T>
struct is_void : public __is_void_helper<typename remove_cv<T>::type>::type {
};

需要注意,这里继承的是: public __is_void_helper<typename remove_cv<T>::type>::type注意,这里用的是type而不是value_type。这里再回顾一下std::integral_constant

template<class T, T v>
struct integral_constant {
static constexpr T value = v;
typedef T value_type;
typedef integral_constant type; // 使用注入的类名
constexpr operator value_type() const noexcept { return value; }
constexpr value_type operator()() const noexcept { return value; } // c++14 起
};

注意这里typevalue_type的差别。

is_void的另一种实现

template< class T >
struct is_void : std::is_same<void, typename std::remove_cv<T>::type> {};

判断内置类型

template<>
struct __is_integral_helper<bool>
: public true_type { };
template<>
struct __is_integral_helper<char>
: public true_type { };
template<>
struct __is_integral_helper<signed char>
: public true_type { };
template<>
struct __is_integral_helper<unsigned char>
: public true_type { };
template<>
struct __is_integral_helper<wchar_t>
: public true_type { };
template<>
struct __is_integral_helper<char16_t>
: public true_type { };
template<>
struct __is_integral_helper<char32_t>
: public true_type { };
template<>
struct __is_integral_helper<short>
: public true_type { };
template<>
struct __is_integral_helper<unsigned short>
: public true_type { };
template<>
struct __is_integral_helper<int>
: public true_type { };
template<>
struct __is_integral_helper<unsigned int>
: public true_type { };
template<>
struct __is_integral_helper<long>
: public true_type { };
template<>
struct __is_integral_helper<unsigned long>
: public true_type { };
template<>
struct __is_integral_helper<long long>
: public true_type { };
template<>
struct __is_integral_helper<unsigned long long>
: public true_type { };
template<>
struct __is_integral_helper<__GLIBCXX_TYPE_INT_N_0>
: public true_type { };
template<>
struct __is_integral_helper<unsigned __GLIBCXX_TYPE_INT_N_0>
: public true_type { };
template<>
struct __is_integral_helper<__GLIBCXX_TYPE_INT_N_1>
: public true_type { };
template<>
struct __is_integral_helper<unsigned __GLIBCXX_TYPE_INT_N_1>
: public true_type { };
template<>
struct __is_integral_helper<__GLIBCXX_TYPE_INT_N_2>
: public true_type { };
template<>
struct __is_integral_helper<unsigned __GLIBCXX_TYPE_INT_N_2>
: public true_type { };
template<>
struct __is_integral_helper<__GLIBCXX_TYPE_INT_N_3>
: public true_type { };
template<>
struct __is_integral_helper<unsigned __GLIBCXX_TYPE_INT_N_3>
: public true_type { };
/// is_integral
template<typename _Tp>
struct is_integral
: public __is_integral_helper<typename remove_cv<_Tp>::type>::type
{ };

这里的实现方式都是与前面提到的类似。

template<typename T>
struct xxx : public __xxx_helper<typename remove_cv<T>::type>::type {
};

基他所有的类型都是通过__is_integral_helper偏特化来实现的。

用同样的方式可以实现is_floating_point

template<typename>
struct __is_floating_point_helper
: public false_type { };
template<>
struct __is_floating_point_helper<float>
: public true_type { };
template<>
struct __is_floating_point_helper<double>
: public true_type { };
template<>
struct __is_floating_point_helper<long double>
: public true_type { };
#if !defined(__STRICT_ANSI__) && defined(_GLIBCXX_USE_FLOAT128)
template<>
struct __is_floating_point_helper<__float128>
: public true_type { };
#endif
/// is_floating_point
template<typename _Tp>
struct is_floating_point
: public __is_floating_point_helper<typename remove_cv<_Tp>::type>::type
{ };

实现思路还是比较清晰的。

#include <iostream>
template<typename T>
struct __is_floating_helper : public std::false_type {};
template<>
struct __is_floating_helper<float> : public std::true_type {};
template<>
struct __is_floating_helper<double> : public std::true_type {};
template<>
struct __is_floating_helper<long double> : public std::true_type {};
template<typename T>
struct is_floating : public __is_floating_helper<typename std::remove_cv<T>::type>::type
{};
int main(void) {
std::cout << is_floating<float>::value << std::endl;
std::cout << is_floating<double>::value << std::endl;
std::cout << is_floating<long double>::value << std::endl;
return 0;
}

另外一种有趣的实现,也可以从cppreference网站得到。

https://zh.cppreference.com/w/cpp/types/is_floating_point

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
> {};

is_array

判断一个类型是否是数组is_array

template<typename>
struct is_array
: public false_type { };
template<typename _Tp, std::size_t _Size>
struct is_array<_Tp[_Size]>
: public true_type { };
template<typename _Tp>
struct is_array<_Tp[]>
: public true_type { };

方法也很简单,就是通过偏特化处理T[N]和T[]

is_pointer

template<typename>
struct __is_pointer_helper
: public false_type { };
template<typename _Tp>
struct __is_pointer_helper<_Tp*>
: public true_type { };
/// is_pointer
template<typename _Tp>
struct is_pointer
: public __is_pointer_helper<typename remove_cv<_Tp>::type>::type
{ };

这里的实现依然是采用偏特化来实现的is_pointer。实际上,偷懒一点也可以采用如下实现:

#include <iostream>
#include <type_traits>
template<typename T>
struct is_pointer : public std::false_type {};
template<typename T>
struct is_pointer<T*> : public std::true_type {};
int main(void) {
std::cout << is_pointer<const int*>::value << std::endl;
std::cout << is_pointer<int const*>::value << std::endl;
return 0;
}

并不需要处理const/volatile等情况。但是真正在实现中,依然处理了这种情况。

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> {};

右引用与右引用

/// is_lvalue_reference
template<typename>
struct is_lvalue_reference
: public false_type { };
template<typename _Tp>
struct is_lvalue_reference<_Tp&>
: public true_type { };
/// is_rvalue_reference
template<typename>
struct is_rvalue_reference
: public false_type { };
template<typename _Tp>
struct is_rvalue_reference<_Tp&&>
: public true_type { };

这里的实现就没有考虑std::remove_cv的情况。所以我觉得前面在查看是否有指针的时候,其实也可以不用remove_cv的。

is_member_object_pointer

判断一个类型是不是类成员指针。检查T是否为非静态成员对象指针。若T是非静态成员对象指针类型,则提供等于 true的成员常量value。否则,value 等于false

template<class T>
struct is_member_object_pointer : std::integral_constant<
bool,
std::is_member_pointer<T>::value &&
!std::is_member_function_pointer<T>::value
> {};

is_member_pointer则是判断一个类型是否是类成员指针这个指针可能是指向某个成员的指针,也有可能是类成员函数指针

is_member_function_pointer而是指一个成员函数指针

而这里的实现相对要复杂一些。

template<typename>
struct is_function;
template<typename>
struct __is_member_object_pointer_helper
: public false_type { };
// 在这里利用偏特化的同时,
// 去掉了成员函数指针
template<typename _Tp, typename _Cp>
struct __is_member_object_pointer_helper<_Tp _Cp::*>
: public integral_constant<bool, !is_function<_Tp>::value> { };
/// is_member_object_pointer
template<typename _Tp>
struct is_member_object_pointer
: public __is_member_object_pointer_helper<
typename remove_cv<_Tp>::type>::type
{ };
// 这里没有必要remove_cv的。

成员函数指针

template<typename>
struct __is_member_function_pointer_helper
: public false_type { };
template<typename _Tp, typename _Cp>
struct __is_member_function_pointer_helper<_Tp _Cp::*>
: public integral_constant<bool, is_function<_Tp>::value> { };
/// is_member_function_pointer
template<typename _Tp>
struct is_member_function_pointer
: public __is_member_function_pointer_helper<
typename remove_cv<_Tp>::type>::type
{ };

成员函数traits的使用方式?在讲使用方式之前,需要先讲一个比较少见的C++的语法。

比如判断一个成员是不是member object pointer需要使用语法int(cls::*)

#include <iostream>
#include <type_traits>
int main() {
class cls {};
std::cout << (std::is_member_object_pointer<int(cls::*)>::value
? "T is member object pointer"
: "T is not a member object pointer") << '\n';
std::cout << (std::is_member_object_pointer<int(cls::*)()>::value
? "T is member object pointer"
: "T is not a member object pointer") << '\n';
}

类成员类型声明

一般写代码的时候,都是如下方式使用类成员。

class X {
int a;
int b;
};
int main(void) {
X x;
x.a; // 这里使用a
x.b; // 这里使用b
}

但是有时候可,可能会用一个变量指向a/b。比如使用一个变量obj_ref指向a/b,使用时,就使用x

x.*obj_ref = 10;

这样就可以给a/b进行赋值了。

#include <iostream>
using namespace std;
class X {
public:
int a;
void f(int b) {
cout << "The value of b is "<< b << endl;
}
};
int main() {
// declare pointer to data member
int X::*ptiptr = &X::a;
// declare a pointer to member function
void (X::* ptfptr) (int) = &X::f;
// create an object of class type X
X xobject;
// initialize data member
xobject.*ptiptr = 10;
cout << "The value of a is " << xobject.*ptiptr << endl;
// call member function
(xobject.*ptfptr) (20);
}

总结

  • type X::*引用的是成员指针。
  • type X::*(函数参数)引用的是类成员的函数。
typedef int X::*my_pointer_to_member;
typedef void (X::*my_pointer_to_function) (int);
int main() {
my_pointer_to_member ptiptr = &X::a;
my_pointer_to_function ptfptr = &X::f;
X xobject;
xobject.*ptiptr = 10;
cout << "The value of a is " << xobject.*ptiptr << endl;
(xobject.*ptfptr) (20);
}

成员的判断

成员的判断主要是有两种

- is_member_object_pointer
- is_member_function_pointer

实际上,也就是用来判断一个给定的类型是不是从属于这两种类型。

#include <iostream>
#include <type_traits>
class X {
int a;
int f(int a, int b) { return 0; }
};
int main() {
std::cout << std::boolalpha;
std::cout << std::is_member_object_pointer<int(X::*)>::value << std::endl;
// or
typedef int X::* x_class_object_pointer_t;
std::cout << std::is_member_object_pointer<x_class_object_pointer_t>::value << std::endl;
return 0;
}

只是需要注意在声明模板的时候,这里需要用到两个参数。

template<typename>
struct is_function;
template<typename>
struct __is_member_object_pointer_helper
: public false_type { };
template<typename _Tp, typename _Cp>
struct __is_member_object_pointer_helper<_Tp _Cp::*>
: public integral_constant<bool, !is_function<_Tp>::value> { };
/// is_member_object_pointer
template<typename _Tp>
struct is_member_object_pointer
: public __is_member_object_pointer_helper<
typename remove_cv<_Tp>::type>::type
{ };

接下来的is_member_function_pointer就也可以顺理成章写出来了。

template<typename>
struct __is_member_function_pointer_helper
: public false_type { };
template<typename _Tp, typename _Cp>
struct __is_member_function_pointer_helper<_Tp _Cp::*>
: public integral_constant<bool, is_function<_Tp>::value> { };
/// is_member_function_pointer
template<typename _Tp>
struct is_member_function_pointer
: public __is_member_function_pointer_helper<
typename remove_cv<_Tp>::type>::type
{ };

总结
无论是类内部的成员指针或者是类内部的函数指针,都可以通过Type Class::*这样来进行定义。只不过这个Type如果是int/float/double等类型或自定义类型,那么就是一个成员指针。如果Type是一个函数指针,那么指向的,就是一个类内部函数指针

编译器支持

接下来三项,就是需要编译器进行支持的。比如__is_enum/__is_union/__is_class这三个都是需要编译器支持的。

/// is_enum
template<typename _Tp>
struct is_enum
: public integral_constant<bool, __is_enum(_Tp)>
{ };
/// is_union
template<typename _Tp>
struct is_union
: public integral_constant<bool, __is_union(_Tp)>
{ };
/// is_class
template<typename _Tp>
struct is_class
: public integral_constant<bool, __is_class(_Tp)>
{ };

is_function则是一个长长的偏特化处理,基本上包含了各种函数的写法。在这里忽略掉。不去展开讲这个宏。

是否是nullptr

template<typename>
struct __is_null_pointer_helper
: public false_type { };
template<>
struct __is_null_pointer_helper<std::nullptr_t>
: public true_type { };
/// is_null_pointer (LWG 2247).
template<typename _Tp>
struct is_null_pointer
: public __is_null_pointer_helper<typename remove_cv<_Tp>::type>::type
{ };
/// __is_nullptr_t (extension).
template<typename _Tp>
struct __is_nullptr_t
: public is_null_pointer<_Tp>
{ };

这里的处理非常简单,就是利用nullptr来完成偏特化。

const & volatile

/// is_const
template<typename>
struct is_const
: public false_type { };
template<typename _Tp>
struct is_const<_Tp const>
: public true_type { };
/// is_volatile
template<typename>
struct is_volatile
: public false_type { };
template<typename _Tp>
struct is_volatile<_Tp volatile>
: public true_type { };

这里需要注意的是,在C++里面,int constconst int这两者是差不多的作用。

平凡和标准部局

/// is_trivial
template<typename _Tp>
struct is_trivial
: public integral_constant<bool, __is_trivial(_Tp)>
{ };
// is_trivially_copyable
template<typename _Tp>
struct is_trivially_copyable
: public integral_constant<bool, __is_trivially_copyable(_Tp)>
{ };
/// is_standard_layout
template<typename _Tp>
struct is_standard_layout
: public integral_constant<bool, __is_standard_layout(_Tp)>
{ };
/// is_pod
// Could use is_standard_layout && is_trivial instead of the builtin.
template<typename _Tp>
struct is_pod
: public integral_constant<bool, __is_pod(_Tp)>
{ };

这里在判断的时候,都是通过编译器内置的接口来进行判断。
除此之外,还有

  • is_literal_type
  • is_empty 这个是指类是否是空的,里面啥都没有。
  • is_polymorphic 是不是多态
  • is_final 是否是final类,不可继承。
  • is_abstract 是否是抽象类

signed判断

template<typename _Tp,
bool = is_arithmetic<_Tp>::value>
struct __is_signed_helper
: public false_type { };
// 这里利用-1这个转换来进行判断
template<typename _Tp>
struct __is_signed_helper<_Tp, true>
: public integral_constant<bool, _Tp(-1) < _Tp(0)>
{ };
/// is_signed
template<typename _Tp>
struct is_signed
: public __is_signed_helper<_Tp>::type
{ };
/// is_unsigned
// 由于前面提供了not操作符
template<typename _Tp>
struct is_unsigned
: public __and_<is_arithmetic<_Tp>, __not_<is_signed<_Tp>>>
{ };