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 将继续考虑第一个版本.

总体来看, 实现的功能是: 先尝试推导 _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个类型的公共类型, 步骤如下

  1. common_type 接收参数 _Ty1_Ty2 后, 托管为 _Common_type2<_Ty1, _Ty2, decay_t<_Ty1>, decay_t<_Ty2>>
  2. 如果 _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>>
  3. 回到 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 是如何递归的?

  1. common_type<_Ty1, _Ty2, _Rest...> 托管为 _Common_type3<void, _Ty1, _Ty2, _Rest...>
  2. 然后 _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
posted @ 2025-08-02 12:18  Ace233  阅读(18)  评论(0)    收藏  举报