boost.Hana 中的 when<> 特性如何工作?

How the when<> trait in boost.Hana works?

我对 std::enable_if 有一些经验。 IIRC,它是关于格式正确的表达式是否导致 true return 返回用户类型 T(如果给定)或通过嵌套类型别名无效。

template<bool,typename = void>
struct enable_if;

template<typename T>
struct enable_if<true,T>{
 using type = T;
};

template <typename T, typename = void>
struct base_template{
     enum { value= false};
};

template <typename T>
struct base_template<T, typename enable_if<std::is_integral<T>::value>::type> {
    enum { value= true};// something useful...
};

struct some{};
static_assert(base_template<some>::value,"F*"); //error: static assertion failed: F
static_assert(base_template<int>::value,"F*");

但是在 boost.Hana 中我看到了这个特性 when<> 并且它的实现就像

template <bool condition>
struct when;

template <typename T, typename = when<true>>
struct base_template{
     enum { value= false};
};

template <typename T>
struct base_template<T, when<std::is_integral<T>::value>> {
    enum { value= true};// something useful...
};

struct some{};

static_assert(base_template<int>::value,"F*");
static_assert(base_template<some>::value,"F*");<source>:28:15: error: static assertion failed: F*

SFINAE 在这里如何运作?尽管 std::is_integral<some>::value 会导致 false,但这并不意味着(确实如此?)when<false>ill-formed 并且应该将实例化分派给主 class模板。我在这里遗漏了什么吗?

大意是一样的。您可以以基本相同的方式使用 enable_if_tdecltype。现在,您可能已经习惯了看到像这样的 SFINAE 偏特化:

template<class T, class U = void>
struct Foo {};

template<class T>
struct Foo<T, decltype(T::bar())> {};

... Foo<X> ...

这里,Foo<X>首先被编译器扩展为Foo<X, void>(因为你在"call site"处没有提供U,所以默认U = void 被填入)。然后,编译器查找 class 模板 Foo 的 best-matching 特化。如果 decltype(X::bar()) 实际上是 void,那么 Foo<T, decltype(T::bar())> [with T=X] 将是 Foo<X, void> 的完美匹配。否则,将使用通用 Foo<T, U> [with T=X, U=void]

现在以 Hana when 为例。

template<bool> struct when {};

template<class T, class U = when<true>>
struct Foo {};

template<class T>
struct Foo<T, when<T::baz>> {};

... Foo<X> ...

这里,Foo<X>首先被编译器扩展为Foo<X, when<true>>(因为你在"call site"处没有提供U,所以默认U = when<true> 被填入)。然后,编译器查找 class 模板 Foo 的 best-matching 特化。如果 when<X::baz> 实际上是 when<true>,那么 Foo<T, when<T::baz>> [with T=X] 将是 Foo<X, when<true>> 的完美匹配。否则,将使用通用 Foo<T, U> [with T=X, U=when<true>]

您可以用任意复杂的布尔表达式替换我示例中的简单表达式 T::baz,只要它是 constexpr-evaluable。在您的原始示例中,表达式为 std::is_integral<T>::value.

我的 CppCon 2017 session "A Soupçon of SFINAE" 介绍了一些类似的例子。