Как я могу проверить тип 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.