前言
从这篇文章并不能学到模板特化的基础,但如果有了模板特化基础知识,看着会很有趣。
就先总结这些,遇到再慢慢补充。接下来,还会补充一些应用实例。
概述
在编译时对类型进行检查,可以将部分代码的处理放在编译阶段。
目前主要分为几个类别:
- integral_constant (优先)
- 类型处理
- 类型检查
- 类型推导
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_t
是decay<_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
定义了三个特例化版本,分别对应于对普通类型、数组类型和函数:
- 普通类型:去掉cv限定符
- 数组类型:退化为指针类型
- 函数类型:退化为函数指针类型
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_const
和add_volaile
分别添加c和v限定符
template<typename _Tp>
struct add_cv
{
typedef typename
add_const<typename add_volatile<_Tp>::type>::type type;
};
2 remove_cv
去除cv限定符
四个特例化版本分别对应四种情况:
- 没有cv限定符
- 有c限定符
- 有v限定符
- 有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 。
- 如果是普通的数据类型,包括内置类型,类、引用类型或各类指针类型。
is_const<const _Tp>::value
是 true。 - 如果是函数类型。
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
调用会触发静态断言。