Amethyst Studio
511 words
3 minutes
C++模板实现MatchIf

我们期待实现如下的效果:

template <typename T, bool = MathIf<T, char, int, long>::value>
void foo() {
  //...
}

也就是这种MatchIf,只有当Tcharintlong的时候才能够实例化。

当然有很多种实现方式,这里介绍的是一种稍微复杂的实现.

Typelist#

首先实现一个Typelist,在这个Typelist的基础上实现FrontPopFront两个Type Function。

template <typename... Types> struct Typelist {};

template <typename List> struct FrontT;

template <typename Head, typename... Tails> struct FrontT<Typelist<Head, Tails...>> {
  using Type = Head;
};

template <typename List> using Front = typename FrontT<List>::Type;

template <typename List> struct PopFrontT;

template <typename Head, typename... Tail>
struct PopFrontT<Typelist<Head, Tail...>> {
  using Type = Typelist<Tail...>;
};

template <typename List> using PopFront = typename PopFrontT<List>::Type;

MatchIfT#

接着我们实现MatchIfT这个Helper,这个的主要作用是,递归查看T与后面的类型是否匹配,如果匹配,那么返回一个true_type,否则返回一个false_type

template <typename T, typename List> struct MatchIfT;

template <typename T, typename... Types>
struct MatchIfT<T, Typelist<Types...>> {
  using First = Front<Typelist<Types...>>;
  using Last = PopFront<Typelist<Types...>>;
  using result_type =
      std::conditional_t<
        std::is_same_v<T, First>,
        std::true_type,
        typename MatchIfT<T, Last>::result_type>;
};

template <typename T> struct MatchIfT<T, Typelist<>> {
  using result_type = std::false_type;
};

但是注意这个MatchIfT还不能直接使用,因为我们的期待是bool = MathIf<...>这种形式,如果直接使用MatchIfT,这个无论如何都会有返回,只是值要么是true要么是false。我们需要做的SFINAE是当match成功时有value域,否则没有value域从而造成代入失败。

也就是说我们有一个EnableMathIfV,其定义是这样:

template <bool> struct EnableMatchIfV {};

template <> struct EnableMatchIfV<true> {
  constexpr static bool value = true;
};

注意这里,对于EnableMatchIfV<false>是没有value域的。

然后,对于true_typefalse_type,实际上是可以直接进行编译期求值的,然后我们将值进行求出,接着再代入到EnableMathIfV里面即可:

template <typename T, typename... Types>
struct MatchIf {
  using result_type = typename MatchIfT<T, Typelist<Types...>>::result_type;
  constexpr static bool v = result_type();
  constexpr static bool value = EnableMatchIfV<v>::value;
};

这样的话,我们就完成了我们的目标,尝试下面的函数:

template <typename T, bool = MatchIf<T, char, int, long>::value>
void baz() {
  std::cout << __PRETTY_FUNCTION__ << std::endl;
}

int main() {
  baz<char>();           // OK
  baz<int>();            // OK
  baz<unsigned int>();   // ERROR!
};
C++模板实现MatchIf
https://ziyue.cafe/posts/matchif-in-cpp/
Author
Kaida Amethyst
Published at
2023-07-18