Это та же самая общая идея.Вы можете использовать enable_if_t
или decltype
в основном таким же образом.Теперь вы, вероятно, привыкли видеть частичные специализации 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>
(потому что вы не указали U
в«сайт вызова», поэтому вместо него указывается значение по умолчанию U = void
).Затем компилятор ищет наиболее подходящую специализацию шаблона класса Foo
.Если 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>>
(потому что вы не указали U
на «сайте вызова», поэтому вместо него введено значение по умолчанию U = when<true>
).Затем компилятор ищет наиболее подходящую специализацию шаблона класса Foo
.Если 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.В вашем исходном примере это выражение было std::is_integral<T>::value
.
Моя сессия CppCon 2017 «Суп СФИНЫ» проходит через несколько похожих примеров.