type_traits学习笔记

前言

从这篇文章并不能学到模板特化的基础,但如果有了模板特化基础知识,看着会很有趣。

就先总结这些,遇到再慢慢补充。接下来,还会补充一些应用实例。

概述

在编译时对类型进行检查,可以将部分代码的处理放在编译阶段。

目前主要分为几个类别:

  1. integral_constant (优先)
  2. 类型处理
  3. 类型检查
  4. 类型推导

1 integral_constant

大部分类型特性都会使用到的。作用:用来存储一个特定类型的编译时常量

  • 下面这部分代码为 integral_constant定义,其他的都是特例化版本。
    value_type为常量的类型
    value为存储的编译时常量的值
  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 noexcept { return value; }
#if __cplusplus > 201103L

#define __cpp_lib_integral_constant_callable 201304

      constexpr value_type operator()() const noexcept { return value; }
#endif
    };

1 bool类型

  /// The type used as a compile-time boolean with true value.      
  using true_type =  integral_constant<bool, true>;

  /// The type used as a compile-time boolean with false value.
  using false_type = integral_constant<bool, false>;

 template<bool __v>
    using __bool_constant = integral_constant<bool, __v>;
  /// @endcond

#if __cplusplus >= 201703L
# define __cpp_lib_bool_constant 201505
  /// Alias template for compile-time boolean constant types.
  /// @since C++17
  template<bool __v>
    using bool_constant = integral_constant<bool, __v>;

2 类型处理

1 decay_t

进行类型退化

  • 这是decay_t的声明,decay_tdecay<_Tp>::type的别名
  template<typename _Tp>                     
    using decay_t = typename decay<_Tp>::type;
  • 这里是decay类的定义。
    首先,除去_Tp类型的引用限定符。
    之后,对去掉引用限定符的__remove_type进行处理
  template<typename _Tp>                
    class decay
    {
      typedef typename remove_reference<_Tp>::type __remove_type;

    public:
      typedef typename __decay_selector<__remove_type>::__type type;
    };
  • 这里是对__remove_type的处理

__decay_selector定义了三个特例化版本,分别对应于对普通类型、数组类型和函数:

  1. 普通类型:去掉cv限定符
  2. 数组类型:退化为指针类型
  3. 函数类型:退化为函数指针类型
  template<typename _Up,                                          
	   bool _IsArray = is_array<_Up>::value, 
	   bool _IsFunction = is_function<_Up>::value> 
    struct __decay_selector; 

  // NB: DR 705. 
  template<typename _Up> 
    struct __decay_selector<_Up, false, false> 
    { typedef __remove_cv_t<_Up> __type; }; 

  template<typename _Up> 
    struct __decay_selector<_Up, true, false> 
    { typedef typename remove_extent<_Up>::type* __type; }; 

  template<typename _Up> 
    struct __decay_selector<_Up, false, true> 
    { typedef typename add_pointer<_Up>::type __type; }; 

2 add_const, remove_const

1 add_const

添加顶层const

  template<typename _Tp>    
    using add_const_t = typename add_const<_Tp>::type;

  template<typename _Tp>  
    struct add_const
    { typedef _Tp const     type; };

2 remove_const

去除顶层const

  template<typename _Tp>      
    using remove_const_t = typename remove_const<_Tp>::type;

  template<typename _Tp>
    struct remove_const
    { typedef _Tp     type; };

  template<typename _Tp>
    struct remove_const<_Tp const>
    { typedef _Tp     type; };

3 add_volatile, remove_volatile

1 add_volatile

添加v限定符

  template<typename _Tp>
    struct add_volatile
    { typedef _Tp volatile     type; };

2 remove_volatile

去除v限定符

  template<typename _Tp>
    struct remove_volatile
    { typedef _Tp     type; };

  template<typename _Tp>
    struct remove_volatile<_Tp volatile>
    { typedef _Tp     type; };

4 add_cv, remove_cv

1 add_cv

添加cv限定符

使用了到 add_constadd_volaile分别添加c和v限定符

  template<typename _Tp>
    struct add_cv
    {
      typedef typename
      add_const<typename add_volatile<_Tp>::type>::type     type;
    };

2 remove_cv

去除cv限定符

四个特例化版本分别对应四种情况:

  1. 没有cv限定符
  2. 有c限定符
  3. 有v限定符
  4. 有cv限定符
  template<typename _Tp>     
    struct remove_cv;

  template<typename _Tp>  
    struct remove_cv
    { using type = _Tp; };

  template<typename _Tp>
    struct remove_cv<const _Tp>
    { using type = _Tp; };

  template<typename _Tp>
    struct remove_cv<volatile _Tp>
    { using type = _Tp; };

  template<typename _Tp>
    struct remove_cv<const volatile _Tp>
    { using type = _Tp; };

3 类型检查

1 is_function

检查该类型是否是函数类型

注意:如果传入的是一个函数指针类型,需要使用 remove_pointer去掉指针

标准库使用了一个技巧,先给传入的类型加 const 关键字修饰,之后再判断该类型是否为 const 。

  1. 如果是普通的数据类型,包括内置类型,类、引用类型或各类指针类型。is_const<const _Tp>::value是 true。
  2. 如果是函数类型。is_const<const _Tp>::value依然是 false。
// 变量模板,简化类模板的书写
template <typename _Tp>          
  inline constexpr bool is_function_v = is_function<_Tp>::value;
  
  template<typename _Tp>
    struct is_function
    : public __bool_constant<!is_const<const _Tp>::value> { };

  template<typename _Tp>
    struct is_function<_Tp&>
    : public false_type { };

  template<typename _Tp>
    struct is_function<_Tp&&>
    : public false_type { };

2 __is_tuple_like

检查该类型是否为tuple类型(之前不知道,都是自己写)

通过 __is_tuple_like_impl 特例化两个版本来分别处理类型为 tuple 和 非tuple 两种情况。

  template<typename> //忽略的类型参数名,因为不使用             
    struct __is_tuple_like_impl : false_type                                     
    { };

  template<typename... _Tps> //将_Tp 特化为 tuple<_Tps...>类型
    struct __is_tuple_like_impl<tuple<_Tps...>> : true_type
    { };
    
  template<typename _Tp>   
    struct __is_tuple_like		//下面是去掉tuple类型的 cv和引用限定符。如果不去掉,并不一定会优先匹配特化版本
    : public __is_tuple_like_impl<__remove_cvref_t<_Tp>>::type
    { };
  • 可以通过定义一个变量模板类简化书写
template <typename _Tp> 
constexpr bool is_tuple_v = std::__is_tuple_like<_Tp>::value;

3 is_const …

有i相关的一系列函数

1 is_const

类型是否被顶层const修饰

  template<typename>
    struct is_const
    : public false_type { };

  template<typename _Tp>
    struct is_const<_Tp const>
    : public true_type { };

4 is_base_of

两个类是否具有继承关系

// 注意基类和派生类的位置
template <typename _Base, typename _Derived>    
  inline constexpr bool is_base_of_v = is_base_of<_Base, _Derived>::value; 

5 is_convertible

两个类型是否可隐式转换

// 注意 左边为源类型,右边为目标类型
template <typename _From, typename _To>
  inline constexpr bool is_convertible_v = is_convertible<_From, _To>::value;

比如:

    bool f = std::is_convertible_v<std::string, std::string_view>; 
    bool f_ = std::is_convertible_v<std::string_view, std::string>;
    // f = true, f_ = false

因为 string 类中定义了隐式转换为 string_view类的运算符函数,反之,没有。

条件检查

1 conditional_t

编译时的条件分支

参数

  • _Cond:是能够编译时得到结果的条件表达式
  • _Iftrue:如果 _Cond 为 true,::type为 _Iftrue
  • _Iffalse:如果 _Cond 为 true, ::type为 _Iffalse
  template<bool _Cond, typename _Iftrue, typename _Iffalse>            
    using conditional_t = typename conditional<_Cond, _Iftrue, _Iffalse>::type;

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

2 enable_if_t

条件成立,则正确执行。条件不成立,则,发生编译时错误。

参数

  • _Cond:是能够编译时得到结果的条件表达式
  • _Tp :如果 _Cond 为 true,::type为 _Tp。如果 _Cond 为false, 就没有 ::type,会发生编译时错误。
  template<bool _Cond, typename _Tp = void>           
    using enable_if_t = typename enable_if<_Cond, _Tp>::type;
    
  template<bool, typename _Tp = void>
    struct enable_if
    { };

  // Partial specialization for true.
  template<typename _Tp>
    struct enable_if<true, _Tp>
    { typedef _Tp type; };

比如:optional类中,有这么一段代码

   template<typename... _Args>
	_GLIBCXX20_CONSTEXPR //修饰函数为constexpr属性
	enable_if_t<is_constructible_v<_Tp, _Args...>, _Tp&>
	emplace(_Args&&... __args)
	noexcept(is_nothrow_constructible_v<_Tp, _Args...>)
	{
	  this->_M_reset();
	  this->_M_construct(std::forward<_Args>(__args)...);
	  return this->_M_get();
	}

enable_if_t<is_constructible_v<_Tp, _Args...>, _Tp&>约束 只有在 _Tp 类型可以由 _Args 参数构造时,该返回值类型才有效。 它不会直接影响函数的行为,但会影响函数是否可以被正确编译和调用。


多个类型的共同检查

1 __and_

类似逻辑运算符 and, 对多个条件进行同时检查。如果全部满足,则返回true,否则,返回false

通过模板的递推展开,来对每一个类型进行检查。

  template<typename... _Bn>          
    inline constexpr bool __and_v = __and_<_Bn...>::value;

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

2 __or_

类似逻辑运算符 or, 对多个条件进行同时检查。如果有一个满足,则返回true,否则,返回false

__and_类似,也是模板递推展开

  template<typename... _Bn>  
    inline constexpr bool __or_v = __or_<_Bn...>::value;

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

3 __not_

类似逻辑运算符 not, 对单个条件进行检查。如果条件为false, 返回true, 否则,返回false

  template<typename _Pp>
    struct __not_
    : public __bool_constant<!bool(_Pp::value)>
    { };

4 类型推导

1 函数相关

模板的实现就不看了,有点乱,有兴趣的可以自己去看看。

1 invoke_result_t

推导出返回值类型

C++11的 result_of废除了

  template<typename _Fn, typename... _Args>       
    using invoke_result_t = typename invoke_result<_Fn, _Args...>::type;

2 is_invocable

判断 函数类型、函数指针类型 或 函数对象类型 是否与参数相匹配

template<typename _Fn, typename... _Args>   
  inline constexpr bool is_invocable_v = is_invocable<_Fn, _Args...>::value;

3 is_invocable_r

判断 在 is_invocable条件下,返回值类型 是否与 Ret相匹配

template<typename _Ret, typename _Fn, typename... _Args> 
  inline constexpr bool is_invocable_r_v
    = is_invocable_r<_Ret, _Fn, _Args...>::value;

4 is_constructible

用于检查 _Tp 类型是否可以使用 _Args 一系列参数进行构造

template <typename _Tp, typename... _Args>
  inline constexpr bool is_constructible_v =
    is_constructible<_Tp, _Args...>::value;

2 declval

用来在decltype中进行类型推导

  • decltype函数模板的定义如下
  template<typename _Tp>                     
    auto declval() noexcept -> decltype(__declval<_Tp>(0))
    {
      static_assert(__declval_protector<_Tp>::__stop,
		    "declval() must not be used!");
      return __declval<_Tp>(0);
    }

有一点需要特别注意declval 只能用于decltype推导。如果在其他地方使用,会触发静态断言。

  • __declval_protector定义如下 :就是把 false封装了一层
  template<typename _Tp> 
    struct __declval_protector
    {
      static const bool __stop = false;
    };

declval在不同的上下文会表现出不同行为的原因是:

  • 在 decltype 中使用,实际上并不会调用declval,只用作类型推导。
  • 如果在其他地方使用,declval调用会触发静态断言。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值