文章目录
- 前言
- 一、源码
- 二、源码解析
- 1. remove_const:移除const标志
- 2. remove_volatile:去除volatile
- 3. remove_cv:去除const和volatile属性
- 4. _Disjunction:(感觉咋描述都不清晰)
- 5. disjunction:对_Disjunction不规格数据的过滤,利用继承方式去提取可变参数。
- 6. _Is_any_of_v: 判断是这个未知的参数是否存在与已知的参数列表里
- 7. is_integral: 判断参数是否兼容整数类型
- 8. is_floating_point :判断是否是浮点数类型
- 9. is_arithmetic :判断是否是数字类型
- 10. remove_reference :移除引用和不改变原有属性基础上加上const属性
- 总结
前言
看过我之前分享过的模板可变参数列表,模板类型推导,模板forward和move里的模板基础知识,你已经对模板基础知识了解的足够多了,现在你就差把这些知识带入真正的实战里。在开始讲解train_type类前,我们还是需要将底层的轮子看一下,不然直接说train_type会出现很多不懂得地方。
一、源码
老规矩先把源码贴出。我下边会对这里边出现的每一个都进行解析。
template <class _Ty>
struct remove_const { // remove top-level const qualifier
using type = _Ty;
};
template <class _Ty>
struct remove_const<const _Ty> {
using type = _Ty;
};
template <class _Ty>
using remove_const_t = typename remove_const<_Ty>::type;
template <class _Ty>
struct remove_volatile { // remove top-level volatile qualifier
using type = _Ty;
};
template <class _Ty>
struct remove_volatile<volatile _Ty> {
using type = _Ty;
};
template <class _Ty>
using remove_volatile_t = typename remove_volatile<_Ty>::type;
template <class _Ty>
struct remove_cv { // remove top-level const and volatile qualifiers
using type = _Ty;
template <template <class> class _Fn>
using _Apply = _Fn<_Ty>; // apply cv-qualifiers from the class template argument to _Fn<_Ty>
};
template <class _Ty>
struct remove_cv<const _Ty> {
using type = _Ty;
template <template <class> class _Fn>
using _Apply = const _Fn<_Ty>;
};
template <class _Ty>
struct remove_cv<volatile _Ty> {
using type = _Ty;
template <template <class> class _Fn>
using _Apply = volatile _Fn<_Ty>;
};
template <class _Ty>
struct remove_cv<const volatile _Ty> {
using type = _Ty;
template <template <class> class _Fn>
using _Apply = const volatile _Fn<_Ty>;
};
template <class _Ty>
using remove_cv_t = typename remove_cv<_Ty>::type;
template <bool _First_value, class _First, class... _Rest>
struct _Disjunction { // handle true trait or last trait
using type = _First;
};
template <class _False, class _Next, class... _Rest>
struct _Disjunction<false, _False, _Next, _Rest...> { // first trait is false, try the next trait
using type = typename _Disjunction<_Next::value, _Next, _Rest...>::type;
};
template <class... _Traits>
struct disjunction : false_type {}; // If _Traits is empty, false_type
template <class _First, class... _Rest>
struct disjunction<_First, _Rest...> : _Disjunction<_First::value, _First, _Rest...>::type {
// the first true trait in _Traits, or the last trait if none are true
};
template <class... _Traits>
_INLINE_VAR constexpr bool disjunction_v = disjunction<_Traits...>::value;
template <class _Ty, class... _Types>
_INLINE_VAR constexpr bool _Is_any_of_v = // true if and only if _Ty is in _Types
disjunction_v<is_same<_Ty, _Types>...>;
#if _HAS_CXX20
_NODISCARD constexpr bool is_constant_evaluated() noexcept {
return __builtin_is_constant_evaluated();
}
#endif // _HAS_CXX20
template <class _Ty>
_INLINE_VAR constexpr bool is_integral_v = _Is_any_of_v<remove_cv_t<_Ty>, bool, char, signed char, unsigned char,
wchar_t,
#ifdef __cpp_char8_t
char8_t,
#endif // __cpp_char8_t
char16_t, char32_t, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long>;
template <class _Ty>
struct is_integral : bool_constant<is_integral_v<_Ty>> {};
template <class _Ty>
_INLINE_VAR constexpr bool is_floating_point_v = _Is_any_of_v<remove_cv_t<_Ty>, float, double, long double>;
template <class _Ty>
struct is_floating_point : bool_constant<is_floating_point_v<_Ty>> {};
template <class _Ty>
_INLINE_VAR constexpr bool is_arithmetic_v = // determine whether _Ty is an arithmetic type
is_integral_v<_Ty> || is_floating_point_v<_Ty>;
template <class _Ty>
struct is_arithmetic : bool_constant<is_arithmetic_v<_Ty>> {};
template <class _Ty>
struct remove_reference {
using type = _Ty;
using _Const_thru_ref_type = const _Ty;
};
template <class _Ty>
struct remove_reference<_Ty&> {
using type = _Ty;
using _Const_thru_ref_type = const _Ty&;
};
template <class _Ty>
struct remove_reference<_Ty&&> {
using type = _Ty;
using _Const_thru_ref_type = const _Ty&&;
};
template <class _Ty>
using remove_reference_t = typename remove_reference<_Ty>::type;
template <class _Ty>
using _Const_thru_ref = typename remove_reference<_Ty>::_Const_thru_ref_type;
template <class _Ty>
using _Remove_cvref_t = remove_cv_t<remove_reference_t<_Ty>>;
#if _HAS_CXX20
template <class _Ty>
using remove_cvref_t = _Remove_cvref_t<_Ty>;
template <class _Ty>
struct remove_cvref {
using type = remove_cvref_t<_Ty>;
};
#endif // _HAS_CXX20
_STD_END
#pragma pop_macro("new")
_STL_RESTORE_CLANG_WARNINGS
#pragma warning(pop)
#pragma pack(pop)
#endif // _STL_COMPILER_PREPROCESSOR
#endif // _XTR1COMMON_
二、源码解析
1. remove_const:移除const标志
template <class _Ty>
struct remove_const { // remove top-level const qualifier
using type = _Ty;
};
template <class _Ty>
struct remove_const<const _Ty> {
using type = _Ty;
};
//利用偏特化去除const,之前介绍过规则,当模板层面找到最符合的后,就会看时候有特化能满足,能则世界使用特花版。正式利用这种特性才能去除const。
template <class _Ty>
using remove_const_t = typename remove_const<_Ty>::type;
//定义一个新类型。
//说个其他的:模板函数和模板类使用方式是不同的,模板函数利用参数就可以实现自动推导出原类型,但是模板类的类型没法根据参数推导出来,都是需要自己手动指示的。这也是我所解析这个源码的作用,他设计了很多方法,就是为了让一个不确定的类型,转化为已知的类型。
2. remove_volatile:去除volatile
(感觉没咋用到过的关键字,只是了解不让编译器优化),和const那个使用方式是一样的,利用特化提取了关键字,重新制定了类型
template <class _Ty>
struct remove_volatile { // remove top-level volatile qualifier
using type = _Ty;
};
//定义一个基类模板,提取类型就交给特化。他的作用就跟递归最后结果输出一样。总要有个结果,不然会递归里是死循环,在这里直接报错了。
template <class _Ty>
struct remove_volatile<volatile _Ty> {
using type = _Ty;
};
//利用特化提取volatile,对去除volatile后的类型重新定义。
template <class _Ty>
using remove_volatile_t = typename remove_volatile<_Ty>::type;
//重新定义一个类型,出去volatile后的类型。
3. remove_cv:去除const和volatile属性
template <class _Ty>
struct remove_cv { // remove top-level const and volatile qualifiers
using type = _Ty;
template <template <class> class _Fn>
using _Apply = _Fn<_Ty>; // apply cv-qualifiers from the class template argument to _Fn<_Ty>
};
//老规矩定义基类表示,给所有结果一个家。(递归的结果类似,最后总要实现一个的。)利用属性特化,根据模板特化优于普通模板被选择,去除属性。
template <class _Ty>
struct remove_cv<const _Ty> {
using type = _Ty;
template <template <class> class _Fn>
using _Apply = const _Fn<_Ty>;
};
//特化const,去除const属性。只有关键字时const的才会调用
template <class _Ty>
struct remove_cv<volatile _Ty> {
using type = _Ty;
template <template <class> class _Fn>
using _Apply = volatile _Fn<_Ty>;
};
//特化volatile,去除特化volatile。只有关键字时去除特化volatile的才会调用
template <class _Ty>
struct remove_cv<const volatile _Ty> {
using type = _Ty;
template <template <class> class _Fn>
using _Apply = const volatile _Fn<_Ty>;
};
//特化const volatile,去除特化const volatile。只有关键字时去除特化const volatile的才会调用
template <class _Ty>
using remove_cv_t = typename remove_cv<_Ty>::type;
//对去除属性后的类型重新定义。
注:模板模板参数用法以及为啥type前每次都需要加typename见链接
4. _Disjunction:(感觉咋描述都不清晰)
- type是bool_constant(true_type和false_type)。记住这点我就好介绍了。(不是我瞎猜的,后续有调用的类,这只是实现真正步骤的一部分,你仔细看会发现,他没有对参数进行处理。)
- 设计的目的是:保存值。当有true_type出现时,类的type就等于true_type,没有则等于false_type
template <class _Ty, _Ty _Val>
struct integral_constant {
static constexpr _Ty value = _Val;
using value_type = _Ty;
using type = integral_constant;
constexpr operator value_type() const noexcept {
return value;
}
_NODISCARD constexpr value_type operator()() const noexcept {
return value;
}
};
//将本身,值和类型分别定义成三个类型。分别为type,value_type和value
//怕有的人忘记去萃取基类
template <bool _Val>
using bool_constant = integral_constant<bool, _Val>;
//对萃取基类的特化,只有类型为bool类型才会被调用
using true_type = bool_constant<true>;
using false_type = bool_constant<false>;
//调用上述模板新定义两个类型。bool类型,值为true和false
template <bool _First_value, class _First, class... _Rest>
struct _Disjunction { // handle true trait or last trait
using type = _First;
};
//定义一个基类,他的作用是1. 当第一个参数为true时 2. 做最后的处理,当参数只有两个时会被调用。无论是什么都会保存下来。
template <class _False, class _Next, class... _Rest>
struct _Disjunction<false, _False, _Next, _Rest...> { // first trait is false, try the next trait
using type = typename _Disjunction<_Next::value, _Next, _Rest...>::type;
};
//对上述基类进行特化,只有第一个参数是false是才会被调用。
//并且递归的调用自身,去处理可变化参数。_Next::value获取当前的值true或者false。
//最后一次因为只有两个参数<false, First>因此不符合特化类<false, False, Next>,因此会调用基类的。
注:不了解可变参数用法的见链接
5. disjunction:对_Disjunction不规格数据的过滤,利用继承方式去提取可变参数。
目的是:就是对_Disjunction又封装了一层。但是为什么又封了一层呢。我推测可能因为参数,disjunction参数是(T)_Disjunction参数(T::value,T),外界传入的参数是T,因此你想让_Disjunction直接处理显然没有又提出一层显得合理。
template <class... _Traits>
struct disjunction : false_type {}; // If _Traits is empty, false_type
//模板基类。对参数是0个的进行了过滤,继承false_type,表述如果参数为0最后的value就是false,也保证了_Disjunction基类不会出现编译错误。
//这里正好补充了_Disjunction里参数不能是0个的限制。并且继承了false_type,使得外界使用的时候不需要感知。因为type也是继承false_type或者true_type,也即是最后无论对错都能返回统一的类型。
template <class _First, class... _Rest>
struct disjunction<_First, _Rest...> : _Disjunction<_First::value, _First, _Rest...>::type {
// the first true trait in _Traits, or the last trait if none are true
};
//当参数大于等于1个时才会被调用。采用继承提取可变参数方式。去使用_Disjunction,最后会返回false_type或者true_type
template <class... _Traits>
_INLINE_VAR constexpr bool disjunction_v = disjunction<_Traits...>::value;
//获取最后得到的值,false或者true,
6. _Is_any_of_v: 判断是这个未知的参数是否存在与已知的参数列表里
输入一个未知类型参数,和多个已知参数,他会判断是这个未知的参数是否存在与已知的参数列表里。在则返回true,不再则返回false。将is_same作为参数传入了disjunction_v
template <class, class>
_INLINE_VAR constexpr bool is_same_v = false; // determine whether arguments are the same type
//编译器推导出是两个类型,就设置错误。
template <class _Ty>
_INLINE_VAR constexpr bool is_same_v<_Ty, _Ty> = true;
//上一个的特化版本,当两个类型一样时就会调用。对变量进行了特化
//编译器推导出是一个类型就设置正确
template <class _Ty1, class _Ty2>
struct is_same : bool_constant<is_same_v<_Ty1, _Ty2>> {};
//当t1和t2类型调用返回true,因为特化匹配优先级大于普通模板
template <class _Ty, class... _Types>
_INLINE_VAR constexpr bool _Is_any_of_v = // true if and only if _Ty is in _Types
disjunction_v<is_same<_Ty, _Types>...>;
//看已知的参数列表里是否存在输入的类型。
//disjunction_v<is_same<_Ty, _Types>...>;实际传入的是
//disjunction_v<is_same<_Ty, T1>, is_same<_Ty, T2>,,,is_same<_Ty, Tn>>
//这个是可变参数列表的使用。...就是重复之前的使用。他会自动提取然后分别调用你指定的那种方式去执行的。
测试demo
#include <iostream>
template <class _Ty, _Ty _Val>
struct integral_constant {
static constexpr _Ty value = _Val;
using value_type = _Ty;
using type = integral_constant;
constexpr operator value_type() const noexcept {
return value;
}
_NODISCARD constexpr value_type operator()() const noexcept {
return value;
}
};
template <bool _Val>
using bool_constant = integral_constant<bool, _Val>;
using true_type = bool_constant<true>;
using false_type = bool_constant<false>;
template <bool _First_value, class _First, class... _Rest>
struct _Disjunction { // handle true trait or last trait
using type = _First;
};
template <class _False, class _Next, class... _Rest>
struct _Disjunction<false, _False, _Next, _Rest...> { // first trait is false, try the next trait
using type = typename _Disjunction<_Next::value, _Next, _Rest...>::type;
};
template <class... _Traits>
struct disjunction : false_type {}; // If _Traits is empty, false_type
template <class _First, class... _Rest>
struct disjunction<_First, _Rest...> : _Disjunction<_First::value, _First, _Rest...>::type {
// the first true trait in _Traits, or the last trait if none are true
};
template <class, class>
_INLINE_VAR constexpr bool is_same_v = false; // determine whether arguments are the same type
template <class _Ty>
_INLINE_VAR constexpr bool is_same_v<_Ty, _Ty> = true;
template <class _Ty1, class _Ty2>
struct is_same : bool_constant<is_same_v<_Ty1, _Ty2>> {};
template <class... _Traits>
_INLINE_VAR constexpr bool disjunction_v = disjunction<_Traits...>::value;
template <class _Ty, class... _Types>
_INLINE_VAR constexpr bool _Is_any_of_v = // true if and only if _Ty is in _Types
disjunction_v<is_same<_Ty, _Types>...>;
int main(void)
{
bool a = _Is_any_of_v<int, bool, float>;
return 0;
}
7. is_integral: 判断参数是否兼容整数类型
template <class _Ty>
_INLINE_VAR constexpr bool is_integral_v = _Is_any_of_v<remove_cv_t<_Ty>, bool, char, signed char, unsigned char,
wchar_t,
#ifdef __cpp_char8_t
char8_t,
#endif // __cpp_char8_t
char16_t, char32_t, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long>;
//就很简单的一个调用,remove_cv_t<_Ty去除掉输入类型的const和volatile属性,然后把所有整形兼容的类型都作为其他参数输入,利用_Is_any_of_v进行比较。
template <class _Ty>
struct is_integral : bool_constant<is_integral_v<_Ty>> {};
//is_integral_v<_Ty>最后会返回false或者true
//最后会是bool_constant<false> 或者 bool_constant<true>
//is_integral::value false或者true;
8. is_floating_point :判断是否是浮点数类型
template <class _Ty>
_INLINE_VAR constexpr bool is_floating_point_v = _Is_any_of_v<remove_cv_t<_Ty>, float, double, long double>;
template <class _Ty>
struct is_floating_point : bool_constant<is_floating_point_v<_Ty>> {};
//等同判断是否是整形类型
9. is_arithmetic :判断是否是数字类型
template <class _Ty>
_INLINE_VAR constexpr bool is_arithmetic_v = // determine whether _Ty is an arithmetic type
is_integral_v<_Ty> || is_floating_point_v<_Ty>;
//分别调用是不是整形和是不是浮点型,只要有一个返回true就为true。
template <class _Ty>
struct is_arithmetic : bool_constant<is_arithmetic_v<_Ty>> {};
//与上述都等同
10. remove_reference :移除引用和不改变原有属性基础上加上const属性
template <class _Ty>
struct remove_reference {
using type = _Ty;
using _Const_thru_ref_type = const _Ty;
};
//非左值和右值引用
template <class _Ty>
struct remove_reference<_Ty&> {
using type = _Ty;
using _Const_thru_ref_type = const _Ty&;
};
//特化左值引用
template <class _Ty>
struct remove_reference<_Ty&&> {
using type = _Ty;
using _Const_thru_ref_type = const _Ty&&;
};
//特化右值引用
template <class _Ty>
using remove_reference_t = typename remove_reference<_Ty>::type;
//移除引用后的类型进行返回
template <class _Ty>
using _Const_thru_ref = typename remove_reference<_Ty>::_Const_thru_ref_type;
//在原有属性上加上const属性
template <class _Ty>
using _Remove_cvref_t = remove_cv_t<remove_reference_t<_Ty>>;
//把const,volatile,&,&&属性去掉。remove_reference_t<_Ty>去除左值或右值引用属性,remove_cv_t去除const和volatile的属性。
#if _HAS_CXX20
template <class _Ty>
using remove_cvref_t = _Remove_cvref_t<_Ty>;
//同上
template <class _Ty>
struct remove_cvref {
using type = remove_cvref_t<_Ty>;
};
//这个应该是为了模板内部使用,统一格式xx::type。
注:看不懂参考我对于模板类型匹配的那篇文章里边对模板类型推导有详细介绍。链接
总结
终于把所有模板基础都解析完了。待把train_type和tuple两个实现在解析完,或者知道大部分原理(即模板的实际应该)。看完这些仅仅是能看懂模板了,还远没有到会使用模板的地步,但是当把train_type和tuple学完我相信对模板的使用绝对不会仅存在看懂层面了。