template<typename _Tp, _Tp __v> struct integral_constant { static constexpr _Tp value = __v; typedef _Tp value_type; typedef integral_constant<_Tp, __v> type; constexpr operator value_type() const { return value; } constexpr value_type operator()() const { return value; } }; 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; a(); return 0; }
|
所以这里需要区别operator type() const
和type 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;
|
总结
使用模板的时候,编译器的输入就是文本里面的字符,是不能存在任何存储空间的变量。因此,模板参数只能使用类型或者是字面常量。
也就是说,模板元编程的输入有两种:
- 类型,比如int, double, float, 以及自定义类型。
- 类型常量,比如自定义了某种类型,然后定义了这种类型的字面常量。或者内建类型的字段常量,比如
false/true
或者constexpr bool cond = true
这里cond也是属生字面常量。
如何通用地定义字面常量
由前面的模板元输入的可以知道,类型有两种,一种是类型
一种是类型对应的常量
。那么是否有方法可以把两者统一起来?
首先看一下支持类型
和常量
的写法。
template<typename T, bool X> SomeCode SomeCode<someType, true> x;
|
在第二个参数这里就必须显示地指定类型。那么如何后面要支持其他通用的类型的常量,比如int
类型的常量,又需要把同样的代码抄一遍。
temlate<typename T, int X> SomeCode 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; };
|
但是使用方式上就会有点麻烦了。
typedef std::integral_constant<bool, true> true_type typedef std::integral_constant<bool, false> false_type SomeClass<int, false_type> aInst; SomeClass<int, std::integral_constant<int,10>> bInst;
|
总结
- 从抽象上来说,代码的实现方直接把
类型字面常量
抽象成一个模板,并且type/value
可以引用到相应的类型和值。
- 服务方
SomeClass
里面在引用到类型和字面常量值的时候,就通过::type/::value
来引用。
- 客户端原本使用
SomeCode<int, 10>
的地方要修改成SomeClass<int, std::integral_constant<int,10>>
。
定义一些常量
typedef integral_constant<bool, true> true_type; 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
template<bool _Cond, typename _Iftrue, typename _Iffalse> struct conditional { typedef _Iftrue type; }; 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_; template<> struct __or_<> : public false_type { }; template<typename _B1> struct __or_<_B1> : public _B1 { }; template<typename _B1, typename _B2> struct __or_<_B1, _B2> : public conditional<_B1::value, _B1, _B2>::type { }; 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_; template<> struct __and_<> : public true_type { }; template<typename _B1> struct __and_<_B1> : public _B1 { }; 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...> { }; 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 { };
|
这里定义的两个类型,需要重点看一下相应的这段注释。
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 { }; 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; } };
|
注意这里type
与value_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 { }; 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 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 { }; 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> {};
|
右引用与右引用
template<typename> struct is_lvalue_reference : public false_type { }; template<typename _Tp> struct is_lvalue_reference<_Tp&> : public true_type { }; 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> { }; template<typename _Tp> struct is_member_object_pointer : public __is_member_object_pointer_helper< typename remove_cv<_Tp>::type>::type { };
|
成员函数指针
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> { }; 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; x.b; }
|
但是有时候可,可能会用一个变量指向a/b
。比如使用一个变量obj_ref
指向a/b
,使用时,就使用x
这样就可以给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() { int X::*ptiptr = &X::a; void (X::* ptfptr) (int) = &X::f; X xobject; xobject.*ptiptr = 10; cout << "The value of a is " << xobject.*ptiptr << endl; (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; 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> { }; 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> { }; 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
这三个都是需要编译器支持的。
template<typename _Tp> struct is_enum : public integral_constant<bool, __is_enum(_Tp)> { }; template<typename _Tp> struct is_union : public integral_constant<bool, __is_union(_Tp)> { }; 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 { }; template<typename _Tp> struct is_null_pointer : public __is_null_pointer_helper<typename remove_cv<_Tp>::type>::type { }; template<typename _Tp> struct __is_nullptr_t : public is_null_pointer<_Tp> { };
|
这里的处理非常简单,就是利用nullptr
来完成偏特化。
const & volatile
template<typename> struct is_const : public false_type { }; template<typename _Tp> struct is_const<_Tp const> : public true_type { }; template<typename> struct is_volatile : public false_type { }; template<typename _Tp> struct is_volatile<_Tp volatile> : public true_type { };
|
这里需要注意的是,在C++
里面,int const
和const int
这两者是差不多的作用。
平凡和标准部局
template<typename _Tp> struct is_trivial : public integral_constant<bool, __is_trivial(_Tp)> { }; template<typename _Tp> struct is_trivially_copyable : public integral_constant<bool, __is_trivially_copyable(_Tp)> { }; template<typename _Tp> struct is_standard_layout : public integral_constant<bool, __is_standard_layout(_Tp)> { }; 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 { }; template<typename _Tp> struct __is_signed_helper<_Tp, true> : public integral_constant<bool, _Tp(-1) < _Tp(0)> { }; template<typename _Tp> struct is_signed : public __is_signed_helper<_Tp>::type { }; template<typename _Tp> struct is_unsigned : public __and_<is_arithmetic<_Tp>, __not_<is_signed<_Tp>>> { };
|