type-traits源码分析-一-

5 篇文章 0 订阅

title: type_traits源码分析(一)
date: 2022-09-26 11:05:50
tags:
- Modern C++
- C++
- C++ Library

type_traits概述

tyep_traits是C++TMP中不可缺少的一部分,也是Modern C++不可缺少的一部分。任何一位C++程序员,如果想学习Modern C++,想学号Modern C++,肯定不能避免学习type_traits。

什么是type_traits?

type_traits,顾名思义,为类型萃取器/类型特征。type_traits是一系列的元函数。在普通函数中,我们传入数据,返回数据。但是在元函数中,我们传入类型/常量,返回类型/常量。

C++ type_traits的实现,也就是元函数的一般形式, 使用结构体+模板(模式匹配+模板偏特化)的方式。

使用<>来传入类型(语法层面的), 使用::value/::type来获得返回值/返回类型(约定俗成的)。
例如std::true_type::value == true,std::false_type::value == false,std::remove_const<const int>::value

所有的type_traits操作均发生在编译时期。

type_trait能够干什么?

type_traits不仅是标准库的重要组成部分,也是模板元编程中的基本技能。

标准库中的类型安全也多亏了type_traits,在各种C++库中也总是能看到type_traits的身影。

type_traits中的约定

type_traits发展了很长的时间,C++标准演化出来了一套规定
通用的规定

  • 使用::value来表示返回值(值元函数)
  • 使用::type来表示返回类型(类型元函数)
  • 每个元函数只能返回一个类型或者一个值
  • 元函数可以同时是值元函数和类型元函数
  • 元函数必须是一个模板类
  • 对于一个type_traits,形如XXX_t的形式是一个类型应用XXX后的返回类型
  • 对于一个type_traits,形如XXX_v的形式是一个类型应用XXX后的返回值(C++17)

对于一元type_traits的规定

  • 接受一个类型参数 + 可选的辅助附加参数
  • 必须可以默认构造
  • 必须可以拷贝构造
  • 必须公开且无歧义
  • 所有的一元元函数都必须从std::integral_constant继承
  • 基本的特征成员不应该被隐藏,而且明确可用

对于二元元函数的规定

  • 接受有两个类型参数 + 可选的辅助附加参数
  • 必须可以默认构造
  • 必须可以拷贝构造
  • 必须公开且无歧义
  • 所有的一元元函数都必须从std::integral_constant的特例化继承
  • 基本的特征成员不应该被隐藏,而且明确可用

对于transformation-traits的规定

  • 接受一个类型参数 + 可选的辅助附加参数
  • 定义一个public的名为type的嵌套类型
  • 没有默认/拷贝构造的要求(可以有,也可以没有)

你会发现标准库的有些地方没有遵守这个标准——那些代码是在这个标准没有成型之前,例如迭代器有关的类型萃取

描述

本篇文章使用gcc编译器,会提到17,20中的type_traits,请确保你所用的编译器支持17,20

对于有些条件编译,不列出源码并且不分析

我会将元函数和type_traits等价起来,如果文中说元函数,你可以联系上下文把其当作type_traits。反之亦然。

实现

标准库对于type_traits的实现,放在了type_traits中

integral_constant

 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; }
    //有constexpr性质,可以编译时期求值
      constexpr value_type operator()() const noexcept { return value; }
    };

我们可以看见integral_constant定义的很简单,记得unix的哲学吗? less is more! 几乎所有的type_traits都会直接的或者间接的继承integral_constant

true_type false_type

true_type,false_type仅仅是integral_constant的别名,定义如下

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

很多的元函数都继承这两个类型,例如is_XXX等元函数。

bool_constant

很好理解

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

conditional

conditional接受三个参数,一个bool值,两个类型参数,作用相当于编译时期的if,可以这样描述

<bool p, T1, T2> p ? T1 : T2

//前置声明
template<bool, typename, typename>
    struct conditional;  
...
/// 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; };

__type_identity

关于个元函数,没有明确的规定,可以教type_is,或者其他的东西。每个标准库的实现都有所不同。作用就是给定一个T类型,返回一个T类型,用于被其他元函数继承。

  template <typename _Type>
    struct __type_identity
    { using type = _Type; };

  template<typename _Tp>
    using __type_identity_t = typename __type_identity<_Tp>::type;

logic traits

合取,析取,否定 traits。其中conjunctiondisjunctionnegation在定义了宏__cpp_lib_logical_traits 201510才启用

__or_

类似or关键字,__or_元函数接受一个参数包,对这些类型的返回值求析取

template<typename...>
    struct __or_; //主模板永远不能被匹配匹配到,所以不用定义

  template<>
    struct __or_<>	//如果没有类型,继承true_type
    : 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 
    //if _B1::value==true,那么不再判断,继承_B1即可,我们关心的是::value的这个返回值。
    //if _B1::value==false, 则继承_B2,此时_B2匹配到接受一个参数的特例化
    { };

  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
    //if _B1::value==true同理
    //if _B1::value==false, 递归的继承__or_<_B2, _B3, _Bn...>
    { };
//这种语法是C++17添加进来的,也很有效的简化了模板元编程的复杂性
template<typename... _Bn>
    inline constexpr bool __or_v = __or_<_Bn...>::value; //__or_v即是类型用用于__or_v后的返回
//__and_v同

__and_

__and_的功能和实现与__or_的功能和实现大同小异

  template<typename...>
    struct __and_;

  template<>
    struct __and_<>
    : public true_type //这里继承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 //这里的类型排列相较于__or_是反的
    { };

  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 //类型排列同上
    { };

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

__not_

__not_对给定类型的::value返回值取反,实现非常的简单

  template<typename _Pp>
    struct __not_
    : public __bool_constant<!bool(_Pp::value)> //这里有一个强转
    { };

conjunction

合取,功能和__or_一样,通过继承其实现。

  template<typename... _Bn>
    struct conjunction
    : __and_<_Bn...>
    { };
template<typename... _Bn>
    inline constexpr bool conjunction_v = conjunction<_Bn...>::value;

disjunction

析取,同上

  template<typename... _Bn>
    struct disjunction
    : __or_<_Bn...>
    { };
   template<typename... _Bn>
    inline constexpr bool disjunction_v = disjunction<_Bn...>::value;

negation

否定,同上

  template<typename _Pp>
    struct negation
    : __not_<_Pp>
    { };
  template<typename _Pp>
    inline constexpr bool negation_v = negation<_Pp>::value;

is_l/r reference

传入一个类型,返回其是不是一个左值/右值引用类型,如果具有,返回值/类型为 true/true_type, 否则返回false/false_type

  /// is_lvalue_reference
  template<typename>		//主模板继承false_type
    struct is_lvalue_reference
    : public false_type { };

  template<typename _Tp>
    struct is_lvalue_reference<_Tp&> //对于左值引用的特例化,继承true_type
    : 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 { };

is_reference

传入一个类型,返回其是不是一个引用类型,如果具有,返回值/类型为 true/true_type, 否则返回false/false_type

template<typename>
struct is_reference; //声明

template<typename _Tp>
struct is_reference 		//左值引用/右值引用
    : public __or_<is_lvalue_reference<_Tp>, 这里用__or_来实现
                   is_rvalue_reference<_Tp>>::type
{ };

is_const

传入一个类型,返回其是否具有const性质,如果具有,返回值/类型为 true/true_type, 否则返回false/false_type。

通过普通的模板特化就可以实现

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

  template<typename _Tp>
    struct is_const<_Tp const> //对于const的特化
    : public true_type { };

注意,对于&/&&is_const的返回类型是一个::false_type,就是所std::is_const<const int &>::value==false

类似的,还有const int *也是一样。因为这样的语义表示的指向的对象是常量性质的,而不是引用/指针本身是常量性质的。

is_volatile

传入一个类型,返回其是否具有volatile性质,如果具有,返回值/类型为 true/true_type, 否则返回false/false_type。

实现方式和is_const大同小异。注意的事项也和is_const是一样的。

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

  template<typename _Tp>
    struct is_volatile<_Tp volatile>
    : public true_type { };

is_function

传入一个类型,返回其是不是一个函数类型,如果具有,返回值/类型为 true/true_type, 否则返回false/false_type。

C++还有这样的功能!这是黑魔法吗?

这不是什么黑魔法,但确实够酷。在你惊叹他的时候,应当了解一下原理。

对于一个T类型,如果其是一个函数/&,对其添加const不会发成任何类型上的变化。例如

void fun() { }
template <typename T>
struct add_const {
  using type = const T;
};
int main() {
	typename add_const<decltype(fun)>::type c;
  return 0;
}

编译器会生成这样的代码

void fun()
{
}

template<typename T>
struct add_const
{
  using type = const T;
};

/* First instantiated from: insights.cpp:10 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct add_const<void ()> 
{
  using type = void (); //类型上没有发生任何变化
};

#endif

int main()
{
  void c();
  return 0;
}

基于这个原理,我们可以轻松实现这个元函数。考虑到对于const T &,const T &&(T不是一个函数类型) 应用is_const:value==false &/&&继承false_type,主模板添加const性质之后判断得到的类型是不是具有const性质。

  template<typename>
    struct is_function;
  /// is_function
  template<typename _Tp>
    struct is_function
    : public __bool_constant<!is_const<const _Tp>::value> { };
	
//对引用考虑即可,对指针添加const变为*const
  template<typename _Tp>
    struct is_function<_Tp&> 
    : public false_type { };

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值