Как я могу проверить тип T среди пакета параметров Ts...?
Я хочу написать функцию, которая возвращает true, если T
является одним из Ts...
template<class T, class... Ts>
bool is_one_of<T, Ts...>();
Например, is_one_of<int, double, int, float>
возвращает true
, а is_one_of<int, double, std::string, bool, bool>
возвращает false
.
Моя собственная реализация
template<class T1, class T2>
bool is_one_of<T1, T2>() {
return std::is_same<T1, T2>;
}
template<class T1, class T2, class... Ts>
bool is_one_of<T1, T2, Ts...>() {
if (std::is_same<T1, T2>) {
return true;
}
else {
return is_one_of<T1, Ts...>();
}
}
Эта проверка кажется мне общей, поэтому мне интересно, есть ли такая функция в стандартной библиотеке.
Ответы
Ответ 1
В вашей собственной реализации одна проблема заключается в том, что C++ не допускает частичной специализации на шаблонах функций.
Вы можете использовать выражение сгиба (которое введено в C++ 17) вместо рекурсивного вызова функции.
template<class T1, class... Ts>
constexpr bool is_one_of() noexcept {
return (std::is_same_v<T1, Ts> || ...);
}
Если вы используете C++ 11, где складное выражение и std::disjunction
недоступны, вы можете реализовать is_one_of
следующим образом:
template<class...> struct is_one_of: std::false_type {};
template<class T1, class T2> struct is_one_of<T1, T2>: std::is_same<T1, T2> {};
template<class T1, class T2, class... Ts> struct is_one_of<T1, T2, Ts...>: std::conditional<std::is_same<T1, T2>::value, std::is_same<T1, T2>, is_one_of<T1, Ts...>>::type {};
Ответ 2
Вы также можете использовать std::disjunction
чтобы избежать ненужного создания шаблона:
template <class T0, class... Ts>
constexpr bool is_one_of = std::disjunction_v<std::is_same<T0, Ts>...>;
После того, как найден соответствующий тип, остальные шаблоны не создаются. Напротив, выражение сгиба создает все из них. Это может существенно повлиять на время компиляции в зависимости от вашего варианта использования.
Ответ 3
Проверьте, входит ли тип T в пакет параметров Ts:
template<class T0, class... Ts>
constexpr bool is_one_of = (std::is_same<T0, Ts>{}||...);
переменная шаблона.
Альтернатива:
template<class T0, class... Ts>
constexpr std::integral_constant<bool,(std::is_same<T0, Ts>{}||...)> is_one_of = {};
Который имеет тонкие различия.
Ответ 4
Другие ответы показывают несколько правильных решений, чтобы решить эту конкретную проблему в четкой и лаконичной форме. Вот решение, которое не рекомендуется для этой конкретной проблемы, но демонстрирует альтернативную технику: В constexpr
вы можете использовать обычные циклы и простую логику для вычисления результатов во время компиляции. Это позволяет избавиться от рекурсии и попытки частичной шаблонной специализации кода OP.
#include <initializer_list>
#include <type_traits>
template<class T, class... Ts>
constexpr bool is_one_of() {
bool ret = false;
for(bool is_this_one : {std::is_same<T, Ts>::value...}) {
ret |= is_this_one;// alternative style: 'if(is_this_one) return true;'
}
return ret;
}
static_assert(is_one_of<int, double, int, float>(), "");
static_assert(!is_one_of<int, double, char, bool, bool>(), "");
Требуется хотя бы С++ 14.