std::common_type 的原理分析
std::common_type是什么?
- 它是可变参数的类型模板, 用于推导出兼容类型
- 其本质是利用
?:运算时的类型转换 和decltype能力.
下面以MSVC实现为例进行分析
源码解析--第一部分
template <class... _Types>
using void_t = void; // 这用于返回void. 它看上去没用, 后面代码中用于做模板特化版本的匹配检查
template <class _Ty1, class _Ty2>
using _Conditional_type = decltype(false ? ::std::declval<_Ty1>() : ::std::declval<_Ty2>());
// 利用?:运算符在编译期类型转换的性质, 推导出::std::declval<_Ty1>() 和 ::std::declval<_Ty2>()的兼容类型
template <class _Ty1, class _Ty2>
struct _Const_lvalue_cond_oper {}; // 如果不能相容, 则为空的
//这是特化的_Const_lvalue_cond_oper, 将优先匹配
template <class _Ty1, class _Ty2>
// 判断const _Ty1&和const _Ty2& 是否可以推导出兼容类型
requires requires { typename _Conditional_type<const _Ty1&, const _Ty2&>; }
struct _Const_lvalue_cond_oper<_Ty1, _Ty2> {
using type = remove_cvref_t<_Conditional_type<const _Ty1&, const _Ty2&>>;
};
template <class _Ty1, class _Ty2, class = void>
struct _Decayed_cond_oper : _Const_lvalue_cond_oper<_Ty1, _Ty2> {};
template <class _Ty1, class _Ty2>
// 这里的void_t<_Conditional_type<_Ty1, _Ty2>> 相当于一个模板约束, 用于检查是否匹配
struct _Decayed_cond_oper<_Ty1, _Ty2, void_t<_Conditional_type<_Ty1, _Ty2>>> {
using type = decay_t<_Conditional_type<_Ty1, _Ty2>>;
};
_Conditional_type是一个别名模板, 它利用了?:运算在编译期的能力, 推导出兼容类型_Decayed_cond_oper有两个版本, 但它们都只需要传入两个模板实参即可, 似乎会有调用歧义. 但需要考虑如下规则- 模板调用优先考虑特化版本, 因此会优先考虑第二个
_Decayed_cond_oper - 第二版本中的
void_t<_Conditional_type<_Ty1, _Ty2>>用于检查, 如果里面的_Conditional_type<_Ty1, _Ty2>推导失败, 则该版本被禁用, 根据 SFINAE 继续考虑第一个版本 - 第一个版本将调用
_Const_lvalue_cond_oper
- 模板调用优先考虑特化版本, 因此会优先考虑第二个
_Const_lvalue_cond_oper也有两个版本, 且模板形参相同, 但第二个版本带有 requires 子句, 会被优先考虑- requires 将检查
const _Ty1&和const _Ty2&是否相容, 如果不相同, 则被禁用, SFINAE 将继续考虑第一个版本.
- requires 将检查
总体来看, 实现的功能是: 先尝试推导 _Ty1 和 _Ty2 的相容类型, 如果不相容, 则尝试对应的 const & 的版本
源码解析--第二部分
template <class... _Ty>
struct common_type;
template <class... _Ty>
using common_type_t = typename common_type<_Ty...>::type;
template <>
struct common_type<> {}; //对于为空的参数, 也是空的
template <class _Ty1>
struct common_type<_Ty1> : common_type<_Ty1, _Ty1> {}; // 只有一个类型, 等价于common_type<_Ty1, _Ty1>
template <class _Ty1, class _Ty2, class _Decayed1 = decay_t<_Ty1>, class _Decayed2 = decay_t<_Ty2>>
struct _Common_type2 : common_type<_Decayed1, _Decayed2> {}; //用于类型退化
template <class _Ty1, class _Ty2>
struct _Common_type2<_Ty1, _Ty2, _Ty1, _Ty2> : _Decayed_cond_oper<_Ty1, _Ty2> {};
template <class _Ty1, class _Ty2>
struct common_type<_Ty1, _Ty2> : _Common_type2<_Ty1, _Ty2> {};
template <class _Void, class _Ty1, class _Ty2, class... _Rest>
struct _Common_type3 {};
template <class _Ty1, class _Ty2, class... _Rest>
struct _Common_type3<void_t<common_type_t<_Ty1, _Ty2>>, _Ty1, _Ty2, _Rest...> : common_type<common_type_t<_Ty1, _Ty2>, _Rest...> {};
template <class _Ty1, class _Ty2, class... _Rest>
struct common_type<_Ty1, _Ty2, _Rest...> : _Common_type3<void, _Ty1, _Ty2, _Rest...> {};
common_type 是可变长的模板. 当多余三个参数时, 用了 _Common_type3 进行递归. 而实际的推导由 _Common_type2 完成
_Common_type2 的原理
_Common_type2 用于推导2个类型的公共类型, 步骤如下
common_type接收参数_Ty1和_Ty2后, 托管为_Common_type2<_Ty1, _Ty2, decay_t<_Ty1>, decay_t<_Ty2>>- 如果
_Common_type2<_Ty1, _Ty2, decay_t<_Ty1>, decay_t<_Ty2>>形式可视为_Common_type2<_Ty1, _Ty2, _Ty1, _Ty2>, 则用_Decayed_cond_oper<_Ty1, _Ty2>推导, 否则回到common_type<decay_t<_Ty1>, decay_t<_Ty2>> - 回到
common_type<decay_t<_Ty1>, decay_t<_Ty2>>后, 再交付给_Common_type2, 此时形式可视为_Common_type2<decay_t<_Ty1>, decay_t<_Ty2>, decay_t<_Ty1>, decay_t<_Ty2>>, 交付给_Decayed_cond_oper推导
_Common_type3 是如何递归的?
common_type<_Ty1, _Ty2, _Rest...>托管为_Common_type3<void, _Ty1, _Ty2, _Rest...>- 然后
_Common_type3优先考虑_Common_type3<void_t<common_type_t<_Ty1, _Ty2>>, _Ty1, _Ty2, _Rest...>- 它这里利用了
void_t<common_type_t<_Ty1, _Ty2>>进行检查, 和之前提到的原理一致 - 如果兼容, 则转为
common_type<common_type_t<_Ty1, _Ty2>, _Rest...>, 此时参数数目减1
- 它这里利用了

浙公网安备 33010602011771号