C++ 的类型萃取

154 阅读1分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

模板偏特化

在正式介绍类型萃取技术之前,还是简单的略过一下模板偏特化

对于一个模板来说,特化分为全特化和偏特化

在模板进行匹配时 会选择最合适的一个进行实例化

template <typename T, typename V> struct E {
  E() { cout << "默认\n"; }
};
template <> struct E<int, int> {
  E() { cout << "全特化\n"; }
};
template <typename T> struct E<T, int> {

  E() { cout << "偏特化\n"; }
};

利用函数偏特化搞事情

总所周知,现在C++20才有的concept ,在没有这个东西之前,我们的模板是非常不安全的
我们可以用一些东西来进行模板约束,那就是模板偏特化来约束

template <typename T, bool v> struct bool_ {
  static constexpr bool ret = v;
  using value_type = T;
  operator bool() { return ret; }
};
using false_ = bool_<bool, false>;
using true_ = bool_<bool, true>;

template <bool v, typename VALUE> struct if_ {};
template <typename TRUE> struct if_<true, TRUE> { using value_type = TRUE; };

template <typename T> struct isIntegral : bool_<T, false> {};
template <> struct isIntegral<float> : bool_<float, true> {};
template <> struct isIntegral<double> : bool_<double, true> {};
template <> struct isIntegral<int> : bool_<int, true> {};
template <> struct isIntegral<long> : bool_<long, true> {};
template <> struct isIntegral<long long> : bool_<long long, true> {};

template <typename T>
auto add(T a, T b) -> typename if_<isIntegral<T>::ret, T>::value_type {
  return a + b;
}

int main() {
  cout << add(1, 2); // ok 
  cout << add("11", "22"); // 编译失败
  return 0;
}

是不是有点熟悉?

这不就是stl中的enable_if的套路吗

没错,就是这个样子

类型萃取

进入正题,C++中有了模板,有了模板偏特化,我们可以干很多事情

比如,类型萃取,说白了,就是根据最合适的,进行萃取它的所有属性

比如

using namespace std;
template <typename T> struct remove_const_v { using type = T; };
template <typename T> struct remove_const_v<const T> { using type = T; };
template <typename T> struct remove_const_v<T *> { using type = T; };
template <typename T> struct remove_const_v<const T *> { using type = T; };
template <typename T> struct traits_v {
  using value_type = typename remove_const_v<T>::type;
};
int main() {
  {
    traits_v<int>::value_type x;
    cout << typeid(x).name() << endl;
  }
  {
    traits_v<int *>::value_type x;
    cout << typeid(x).name() << endl;
  }
  {
    traits_v<const int>::value_type x;
    cout << typeid(x).name() << endl;
  }
  {
    traits_v<const int *>::value_type x;
    cout << typeid(x).name() << endl;
  }
  return 0;
}

上述例子利用模板特性把T的真正值类型给萃取出来了

同样的道理,我们可以去萃取他的指针类型

比如下面这个更加全面的STL里面的迭代器的萃取类型

template<typename _Category, typename _Tp, typename _Distance = ptrdiff_t,
          typename _Pointer = _Tp*, typename _Reference = _Tp&>
struct iterator {
  typedef _Category  iterator_category;
  typedef _Tp        value_type;
  typedef _Distance  difference_type;
  typedef _Pointer   pointer;
  typedef _Reference reference;
};

迭代器的萃取机制的目的也是一样的,就是为了将值类型,指针类型,引用类型给萃取出来

比如我要看当前类型是不是void,c++中是这么做的

template<typename _Tp>
struct is_void
    : public __is_void_helper<__remove_cv_t<_Tp>>::type
{ };
template <typename _Tp>
inline constexpr bool is_void_v = is_void<_Tp>::value;
template<typename>
struct __is_void_helper
    : public false_type { };

template<>
struct __is_void_helper<void>
    : public true_type { };

更多c++提供的type_traits 请看这儿 zh.cppreference.com/w/cpp/meta