type_traits

  1. 通过传入非类型模板参数,定义了整型常量这种类型
  2. 以此为基础定义了true和false这两种常量类型,及其模板版本,即bool_constant<…>
  3. 实现了conditional_t<bool,type1,type2>,其成员类型type根据bool来等于type1或type2
  4. 实现了__type_identity_t来返回T这一类型,来实现更加复杂的类型运算,为了更容易生成对象?
  5. 实现了__or_,_and,__not_三个模板元编程,参数为<…>,通过前述的bool_constant和conditional_t来实现。并提供他们的别名conjunction,disjunction,negation。这些返回的都是bool常量类型类型,因此需要::value来获取他们的值,而后提供了…_v版本来直接获取其bool值。
  6. 定义is_const和is_violate(const放在type前和type后表达的意思一致,但是最好放在右侧,从右往左读更易于理解)。
  7. 定义is_pointer,is_lvalue_reference,is_rvalue_reference
  8. 定义有着大小的type和(引用,function,空,is_array_unknown_bounds)为完整的或未绑定的类型,可通过__is_complete_or_unbound来获取bool常量类型。
  9. 定义了__success_type<…>有type类型=…,而__failure_type<…>没有type类型。
  10. 定义remove_cv_t<…>调用remove_cv::type来直接返回…去掉cv的版本,为后面的类型萃取做准备。
  11. 定义is_void,只有void,和其cv版本is_void为true_type。
1
2
3
4
5
6
7
8
9
10
11
12
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<__remove_cv_t<_Tp>>::type
{ };
  1. 定义is_integral,方法如前。其中,除了基本类型还定义了__GLIBCXX_TYPE_INT_N_0\1\2\3

    为integral。

  2. 定义is_floating_point,包括float128。

  3. 定义is_array,包括array[size]和array[]。

  4. 定义is_member_pointer,其萃取的方式为_Tp Cp::,这是一个类型,表示*Cp这个类型中类型为_Tp的成员(函数指针或成员)

  5. 定义is_member_function_pointer和is_member_object_pointer,就是和is_member_pointer一致,在萃取后再通过is_function来区分。

  6. 定义is_function,有左值引用或者右值引用的或者它的const版本不是const的就是function?

  7. 定义is_null_pointer,只有nullptr_t及其cv版本。

  8. 定义is_reference,包括左值引用和右值引用

  9. 定义is_arithmetic(算数型),包括整形和小数

  10. 定义is_fundamental(基础类型),包括算数型,空,和空指针

  11. 定义is_object,非function,引用和空类型

  12. 定义is_scalar(A scalar type is a type that has built-in functionality for the addition operator without overloads),包括算数型,枚举,指针,member_pointer,空指针。

  13. 定义is_compound,非基础类型。

  14. 定义is_same,两个版本,is_same<Tp,Tp2>继承false,is_same<Tp,Tp>继承true?

  15. 定义is_one_of<type1,type2…>,即是否与后面的任意一个类型相同。

  16. 定义is_signed_integer,is_unsigned_integer。

  17. 定义is_standard_integer,包括signed和unsigned integer。

  18. 定义void_t<…>,对于任何类型都=void。

  19. 定义is_referenceable<Tp,=void_t<Tp&>>,通过void_t来判断Tp&到底存不存在,这样,referenceable的类型可以实例化这个模板,而非的就不能,因为不能实例化void_t<Tp&>,能左引就能右引。

  20. 声明is_trivial,is_trivially_copyable,is_standard_layout(都需要是完整类型)。

  21. 声明is_empty,is_polymorphic,is_final。

  22. 定义is_signed<Tp,bool=is_arithmetic::value>来限定要满足is_arithmetic,然后继承integral_constant<bool,Tp(-1)<Tp(0)>,和is_unsigned。

  23. 定义declval(int/long),将其返回其类型的一个右值引用,如果Tp是右值引用的话,方式是调用declval<…>(0),declval(int)返回增加右值引用,declval(long)返回原类型,0即能匹配long也能匹配int。因为要函数不同就必须形参不同。这通过不同的形参的方式好像是比较老了,c++11后就可以使用void_t来进行更简单的处理。

  24. 声明declval(),调用declval(int/long),使用的时候应该都是declval通过引用折叠来获取Tp&&这一类型,当且仅当declval_protector::stop为true时。

  25. 定义extent<…,num=0>,如果num=0则返回当前最外维大小(unbounded就是0),否则类似rank递归去掉一维,往外走一维。达到返回第n维大小的效果。

  26. 定义remove_extent,用Tp[]和Tp[size]去匹配。

  27. 定义remove_all_extents,即remove_extent的递归版本。

  28. 定义is_array_known_bounds(知道第0维大小的array),即extent<…>::value>0。

  29. 定义is_array_unknown_bounds,首先要is_array,并且extent不知道。

  30. 定义is_destructible_impl(是否能调用析构函数),通过declval来获取引用来在不创造实例的情况下调用析构函数,就可以判断是否能调用析构函数了(析构函数返回本类型)。

  31. 定义is_destructible_safe,空,不知边界的数组和function都是不可的,引用和scalar都是可以的,而其余的通过remove_all_extents规约到前面两种情况。

  32. 定义is_destructible,首先要是完整的或者无边界数组,并且is_destructible_safe的。

  33. 上述三个还有要判断是否nothrow的版本,即在is_nt_destructiable_impl中调用析构函数时套一个noexcept()。

  34. 定义is_constructible<Tp,args…>,is_default_constructible,都要时cp_or_ub(complete or unbounded)类型的,再像前面一样通过引用调用构造函数即可,default_constructible就是没有args…。

  35. 定义is_copy_constructible,拷贝构造函数的参数就是const Tp&,因此首先判断是否referencable,然后是否constructible<Tp,const Tp&>即可。

  36. 定义is_move_constructible,与copy一致,只不过最后是constructible<Tp,Tp&&>。

  37. 定义上述三个constructible的nothrow版本。

  38. 声明is_(/copy/move)_assignable及其nothrow版本。

  39. 声明is_trivially_constructible。

  40. 定义is_implicitly_default_constructible,即能否使用{}默认构造对象。由于const &能接受临时对象,因此尝试传入{}作为const Tp&。

  41. 声明is_trivially_copy_constructible,is_trivially_move_constructible,is_trivially_(/copy/move)_assignable。

  42. 声明is_trivially_destructible(有析构且有trivially_destructor)。

  43. 声明has_virtual_destructor。

  44. 定义alignment_of=integral_constant<size_t,alignof(Tp)>,即大小为其align的整型常量。

  45. 定义rank,即几维的数组,通过Tp[]和Tp[size_t]去递归萃取。

  46. 声明is_base_of

  47. 定义is_convertible(能否隐式转换),void转void是可行的,此外,function和array不能作为隐式转换的结果对象的。此外,尝试能否用From这个Type的引用调用构造函数获得的对象调用func(To(这个Type))。及其noexcept版本。

  48. 定义remove_const和remove_volatile,方法类似extent,即通过Tp const和Tp volatile来萃取Tp。

  49. 定义add_const,add_volatile和add_cv。

  50. 定义remove_reference,add_(l/r)value_reference(借助is_referencable)。

  51. 定义cv_selector<type,bool,bool>,第一个bool代表const,第二个代表volatile,将type加上对应的const/volatile如果为真。

  52. 定义match_cv_selector<type1,type2>,根据type1的cv情况对type2进行cv_selector。

  53. 定义make_unsigned的所有integral版本,(浮点数是没有unsigned版本的)。

  54. 定义通用的make_unsigned,对所有enum类型和integral类型。但对于bool特化没有make_unsigned。对于所有的类型,都先去掉cv标识符(remove_cv)再进行匹配,匹配完后再通过cv_selector匹配回来。匹配的方法:建立一个可匹配类型,list<所有可匹配的类型>,进行一一匹配。每一次匹配,获取首类型的大小,若≤,则匹配成功,否则去掉第一个,进行递归。在这种情况下w_char,char8/16/32_t这些没有unsigned版本的类型也用有其对应类型,如char16_t对应unsigned int。

  55. 定义通用的make_signed,同样对于bool特化没有。匹配的方式是,转化为unsigned,在将其对应的unsigned类型通过前面普通的特化版本来转化为signed版本。

  56. 定义remove_pointer,通过T*匹配,会将顶层(应该是顶层)的const和volatile去掉,而remove_reference就不会。

  57. 定义add_pointer,只能给referencable和void加*。因为实际上reference本身就是一种“指针”。加的过程就是先remove_reference再直接加*。

对齐大小

假如说对齐大小已经定下来了,并且现在已经放入了一些数据,如果准备加入一个新的数据,如果连着放会导致它被对齐大小的整数倍截断,那么,就会直接跳到对齐大小的整数倍放。对齐大小一般就是结构体中最大类型的对齐大小。总体的目的就是不被截断,能一次读到。提高效率。

  1. 定义最大对齐存储aligned_storage_msa。
1
2
3
4
5
union __type {
unsigned char __data[_Len];
struct __attribute__((__aligned__)) {
} __align;
};

通过union使得data和struct共享存储空间。由于unsigned char的大小为1字节,因此,相当于申请了大小为Len字节的空间。又因为共享空间的结构体启用了最大对齐,因此,这段空间也就启用了最大对齐。

  1. 定义对齐大小不同的存储空间aligned_storage<len,对齐大小>,方法如上。

  2. 定义strictest_alignment<types…>,两个size_t成员,s_alignment和s_size,存储的是所有的type中最大的对齐大小和最大的大小(比如结构体有三个成员,那么大小可能就不止是对齐大小)。求的方式就是模板元编程递归版本的打擂台。

  3. 定义aligned_union,使它的大小为strictest_alignment的s_size和要求的Len中的大者。而对齐就是strictest_alignment的s_alignment,并将aligned_storage<S_len,s_alignment>作为它的存储方法。

  4. 定义decay_selector(进行等价于按值传递函数实参进行的类型转换)。去掉cv符和引用,对于array,去掉一维并转为指针,对于函数,转为指针。

  5. 定义strip_reference_wrapper,将一个被reference_wrapper包裹的类型的reference_wrapper转为真正的引用。

  6. 定义enable_if<bool,Tp>,Tp就是在bool成立时::type的值,如果不成立就不存在::type,通过这种方式可以以返回值,模板参数,形参类型等为参数对函数的范围进行约束。相当于在Tp不改变的情况下凭空加上限制条件,或者直接加在后面enable_if,因为Tp默认是void。

  7. 定义Require<…type>,即满足多个条件的enable_if。

  8. 定义common_type<type…>(这些类型都能够隐式转换到的类型)。关键在于下面这句话

    *decltype*(*true* ? std::declval<_Tp>() : std::declval<_Up>())

    由于其中的三元判断既可能输出_Tp类型也可能输出_Up(无关前面是否是个固定的条件),因此,输出的类型必须是_Tp和_Up的共同类型。再decltype就可以获取其共同类型了。

    在c++20中,*decltype*(*true* ? std::declval<const _Tp&>() : std::declval<const _Up&>())的引入解决了有const和&因与remove_cv版本类型不一致而导致无法获取共同类型的问题(如const int&和int&应该有共同类型const int&)。

    为了让这两个同时起效,定义两个函数,每个函数分别有其更劣的重载版本(…),第一个的更劣版本返回第二个函数decltype的结果,第一个函数判断第一个部分,第二个函数判断第二个部分,成功则返回success_type包裹common_type,否则返回failure_type。

    对于多个类型,递归地取前两个并用common_type来替换,并保证common_type是success的。

  9. 对于enum类型声明underlying_type(enum的元素类型)。

  10. 声明declval_protector,若其成员stop为false则不能进行declval()。

  11. 定义invoke_result<functor,args>(其type为这个函数这个args的情况下的返回类型),其中包括普通函数,function包括的对象,成员函数,和类的数据成员(调用的形参的类别是这个类)。此外,还包括其对应的函数指针,函数引用等等。最基本的实现很简单如下:

    1
    2
    3
    4
    5
    6
    template<typename T,typename... args>
    struct get1;
    template<typename a,typename b,typename... args>
    struct get1<a b::*,args...>{
    using type_=a;
    };

    通过a b::*来萃取出其返回类型。

    但是为了适配指针,函数引用等等,实现上进行了一些加工。

    有以下几个要注意的点

    1. decltype(std::declval<_Fn>()(std::declval<_Args>()...)通过调用来测试这个函数是否存在
    2. 将所有的类型分为,普通函数,数据成员指针和成员函数指针,分别进行处理,对于指针和引用来说在declval的时候也要注意区别。
    3. 对于成员来说,验证提供的类是不是拥有这个函数或成员。
  12. 定义inv_unwrap,类似strip_reference_wrapper,但是strip_reference_wrapper只会去掉一层wrapper,而inv_unwrap会将相关的wrapper外层的cvr都去掉,并去掉这一层。

  13. 定义detector_or<default,void<op>(void),<template args…>Op,args>,如果有以args为参数的实例化,则返回op<args…>,否则返回default这个type。

  14. 声明is_tuple_like。

  15. 定义swappable及nothrow版本,必须为unbounded或complete类型,要求swap(其declval版本)是可行的,并且反过来也要。并且单独对相同类型的swap进行了特判swap。

  16. 定义is_invocable及nothrow版本,即这样调用invoke是否可行。对于没有限定返回类型的invoke调用,只需要判断调用invoke_result返回的type是否是个正常的类型。对于有限定返回类型的invoke调用,判断限定的类型是否正常,接着构造一个函数返回值为result::type的函数(无需函数体),将其返回值传给一个被构造出来的参数为限定返回类型的模板函数,如果一样就能被构造出来,在这一层外再套一层decltype来保证它不被实际执行,也就不需要函数体。之所以不适用declval来调用而是使用构造一个函数的方式是因为declval会返回一个右值引用而右值引用会阻止复制省略,而构造函数不会,它允许。虽然不知道为什么这里需要允许复制省略。

  17. 定义call_is_nt(是个函数),分类讨论5大情况,成员函数/成员指针及其对应引用,和其他情况,分别尝试,返回其noexcept(调用这个对象)的结果,一个普通的bool类型。

  18. 声明nonesuchbase,和nonesuch(一个析构,拷贝构造及拷贝赋值都被delete的一个类)。

  19. 声明is_aggregate。

  20. 声明has_unique_object_representation。

  21. 定义了很多用来匹配或寻找共同c/v/ref。


type_traits
https://lhish.github.io/project/hide/type_traits/
作者
lhy
发布于
2024年6月30日
许可协议