Я предлагаю также совершенно другое решение, которое использует свертывание шаблонов (поэтому, к сожалению, только C ++ 17 или новее) вместо рекурсии шаблонов.
template <typename...>
struct sae_helper;
template <typename ... Ts, typename ... Us>
struct sae_helper<std::tuple<Ts...>, std::tuple<Us...>>
: public std::bool_constant<(std::is_same_v<Ts, Us> || ...)>
{ };
template <typename ... Ts>
struct some_adjacent_equal
: public sae_helper<std::tuple<void, Ts...>, std::tuple<Ts..., void>>
{ };
Если void
является возможным типом в списке типов для проверки, то для вызова sae_helper
из some_adjacent_equal
вместо void
необходимо использовать другой тип, очевидно.
Я полагаю, что это решение предпочтительнее рекурсивного, когда список типов очень длинный, потому что избегают ограничений рекурсии шаблона компилятора.
Если вы можете использовать C ++ 14, вы можете использовать функцию constexpr
вместо свертывания шаблона (и тип тега вместо void) следующим образом
template <typename ... Ts, typename ... Us>
constexpr bool sae_helper (std::tuple<Ts...> const &,
std::tuple<Us...> const &)
{
using unused = bool[];
bool ret { false };
(void)unused { true, ret |= std::is_same<Ts, Us>::value... };
return ret;
}
struct no_type
{ };
template <typename ... Ts>
struct some_adjacent_equal
: public std::integral_constant<bool, sae_helper(std::tuple<no_type, Ts...>{},
std::tuple<Ts..., no_type>{})>
{ };
но, таким образом, вы теряете короткое замыкание или оценку.