C++ 类型萃取(type trait)速学指南

通常我们认为 C++ 的模板主要是为了代码复用,即如果一组相似的类型 T 适用于一套算法,模板可以为类型 T 在编译期生成各自的算法代码,且不会有运行时开销。比如 C++ 的 STL 库,各类查找算法对不同容器都是通用的。

由于模板在编译期的各种特性,除了代码复用以外,模板也可有其他用途。例如,类模板可以被特化(specialization)和偏特化(partial specialization),并且编译器以 SFINAE(Substitution failure is not an error)的方式匹配模板,就可以做类型萃取(type trait);有了 constexpr 和非类型模板参数(non-type template parameter),就可以做复杂的编译期计算,提高运行时性能(但通常会让编译过程变慢,算是一种 trade off)。

所谓类型萃取,就是在编译期确定一个类型 T 是否具备某些特性(trait)。需要注意,“特性”并不是“类型”或者“接口”,不要用“类型”或“接口”去理解 trait,虽然它们很像,而且往往看起来一样。举一个“特性”的例子,比如我们想实现一个函数 func<T>(T t),但是我想要类型 T 必须有成员函数 StartTimer() 和 静态成员 变量 DefaultIntervalMs,那么这里我们对类型 T 的要求,就是 trait。一个 trait 可以在一个新的维度,将一组类型划分为一类。

如果了解了特化和偏特化,以及 SFINAE,类型萃取其实非常简单,这些基础内容可以参看 C++ Template 进阶指南,我在参考链接里也会附上一些资料。这里假设我们已经了解了:

  1. 模板类的特化和偏特化,模板函数的特化,函数的重载
  2. 模板代码中的 dependent namestypename 关键字的用法
  3. SFINAE
  4. unevaluated operands/expression/context, 以及 decltypestd::declval 的用法

对以上这些内容有一个感性认识,然后再多实践几次就能看懂很多类型萃取的代码了。大概搞懂以上内容后,可以继续往下看。

我把常见到的类型萃取技巧分为两种,一种使用类的特化和偏特化,另一种使用函数重载和 decltype。这两种技巧本质上都是基于 SFINAE

假设我们要通过 is_variant<T> 来判断类型 T 是否为 std::variant<Ts...>,如何实现 is_variant<T> 呢?

方法一

is_variant<T> 为例,如果是使用类的特化和偏特化,我们可以这样写:

template <typename T>
struct is_variant_impl : std::false_type {
   
   }; // 默认实现 (primary template)

template <typename... Ts>
struct is_variant_impl<std::variant<Ts...>> : std::true_type {
   
   }; // 偏特化,T 对应 `std::variant<Ts...>`

template <typename T>
using is_variant = is_variant_impl<T>;

int main() {
   
   
   std::cout << std::boolalpha << "is_variant<int> = " << is_variant<int>::v
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值