Как сделать variadic is_same?
Как я могу создать шаблон класса, который возвращает, является ли какой-либо его вариационный тип равным первому типу. Я хочу иметь возможность сделать это:
is_same<T, A, B, C>::value; // true if T is one of A, B or C
И если T
равно любому из этих типов, его статический член value
будет true
, иначе false
. Как я могу это сделать?
Ответы
Ответ 1
Использовать рекурсию шаблона:
template<typename T, typename... Rest>
struct is_any : std::false_type {};
template<typename T, typename First>
struct is_any<T, First> : std::is_same<T, First> {};
template<typename T, typename First, typename... Rest>
struct is_any<T, First, Rest...>
: std::integral_constant<bool, std::is_same<T, First>::value || is_any<T, Rest...>::value>
{};
static_assert(is_any<int, char, double, int>::value, "error 1"); // OK
static_assert(is_any<int, char, double, short>::value, "error 2"); // error
Ответ 2
Красиво и лаконично с С++ 17:
template <class T, class... Ts>
struct is_any : std::disjunction<std::is_same<T, Ts>...> {};
И двойной:
template <class T, class... Ts>
struct are_same : std::conjunction<std::is_same<T, Ts>...> {};
Вариант, который использует выражения сгиба:
template <class T, class... Ts>
struct is_any : std::bool_constant<(std::is_same_v<T, Ts> || ...)> {};
template <class T, class... Ts>
struct are_same : std::bool_constant<(std::is_same_v<T, Ts> && ...)> {};
Ответ 3
Что-то вроде этого. Во-первых, небольшая библиотека метапрограммирования, потому что она добавляет как 2 строки, чтобы сделать это в общем:
template<template<typename,typename>class checker, typename... Ts>
struct is_any_to_first : std::false_type {};
template<template<typename,typename>class checker, typename T0, typename T1, typename... Ts>
struct is_any_to_first<checker, T0, T1, Ts...> :
std::integral_constant< bool, checker<T0, T1>::value || is_any_to_first<checker, T0, Ts...>::value>
{};
Затем двухстрочная реализация is_any_same_to_first
:
template<typename... Ts>
using is_any_same_to_first = is_any_to_first< std::is_same, Ts... >;
И для полноты, оригинал is_all
, который также может оказаться полезным:
template<template<typename,typename>class checker, typename... Ts>
struct is_all : std::true_type {};
template<template<typename,typename>class checker, typename T0, typename T1, typename... Ts>
struct is_all<checker, T0, T1, Ts...> :
std::integral_constant< bool, checker<T0, T1>::value && is_all<checker, T0, Ts...>::value>
{};
template<typename... Ts>
using is_all_same = is_all< std::is_same, Ts... >;
Живой пример is_all_same
.
Обратите внимание, что вызов is_any_same_to_first
чего-то менее явного вызывает проблемы. 2/3 людей, которые пытались ответить на этот вопрос, включая меня, предположили, что is_same<A,B,C>
верно, если все три имеют одинаковый тип!
Ответ 4
В С++ 17 у вас есть еще более приятное решение, использующее переменные шаблона и выражения сгиба:
template<class T, class... Rest>
inline constexpr bool are_all_same = (std::is_same_v<T, Rest> && ...);
И использование также проще, чем все другие примеры:
are_all_same<T, A, B, C>
Нет ::value
, без скобок!
Ответ 5
Используя расслабленные функции constexpr в С++ 14, такие вещи гораздо проще кодировать и, вероятно, гораздо быстрее компилировать, так что вы можете написать:
template <class T, class ... Candidates>
constexpr bool is_all_same() {
bool pairs[] = {std::is_same<T,Candidates>::value...};
for(bool p: pairs) if(!p) return false;
return true;
}
template <class T, class ... Candidates>
constexpr bool is_any_same() {
bool pairs[] = {std::is_same<T,Candidates>::value...};
for(bool p: pairs) if(p) return true;
return false;
}
Это связано с тем, что в С++ 14 функции constexpr могут иметь циклы for.
Ответ 6
Наиболее общая версия, которая работает:
template <typename First, typename Second, typename ... Next>
struct is_same {
template <typename A, typename B>
struct is_same_min {
enum { value = false };
};
template <typename A>
struct is_same_min<A,A> {
enum { value = true };
};
template <typename X, typename Y>
constexpr static bool check() {
return is_same_min<X,Y>::value;
};
template <typename X, typename Y, typename Z, typename ... K>
constexpr static bool check() {
return is_same_min<X,Y>::value and check<Y, Z, K...>();
};
enum { value = check<First, Second, Next...>() };
};
Просто используйте is_same<T1,T2,T3...>::value