web-dev-qa-db-fra.com

Comment puis-je vérifier que le type T est parmi le pack de paramètres Ts ...?

Je veux écrire une fonction pour retourner true si T est l'un des Ts...

template<class T, class... Ts>
bool is_one_of<T, Ts...>();

Par exemple, is_one_of<int, double, int, float> renvoie true et is_one_of<int, double, std::string, bool, bool> renvoie false.

Ma propre implémentation est

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...>();
    }
}

Cette vérification me semble commune donc je me demande s'il y a déjà une telle fonction dans la bibliothèque standard.

38
Yixing Liu

Dans votre propre implémentation, un problème est que C++ n'autorise pas la spécialisation partielle sur les modèles de fonction.

Vous pouvez utiliser l'expression de repli (qui est introduite dans C++ 17) au lieu de l'appel de fonction récursive.

template<class T1, class... Ts>
constexpr bool is_one_of() noexcept {
    return (std::is_same_v<T1, Ts> || ...);
}

Si vous utilisez C++ 11 où expression de pli et std::disjunction ne sont pas disponibles, vous pouvez implémenter is_one_of comme ça:

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 {};
40
Shaoyu Chen

Vous pouvez aussi utiliser std::disjunction pour éviter l'instanciation inutile des modèles:

template <class T0, class... Ts>
constexpr bool is_one_of = std::disjunction_v<std::is_same<T0, Ts>...>;

Une fois le type correspondant trouvé, les modèles restants ne sont pas instanciés. En revanche, une expression de pli les instancie tous. Cela peut faire une différence significative dans le temps de compilation en fonction de votre cas d'utilisation.

30
L. F.

Vérifiez si le type T fait partie du pack de paramètres Ts:

template<class T0, class... Ts>
constexpr bool is_one_of = (std::is_same<T0, Ts>{}||...);

variable de modèle.

Alternative:

template<class T0, class... Ts>
constexpr std::integral_constant<bool,(std::is_same<T0, Ts>{}||...)> is_one_of = {};

Ce qui a de subtiles différences.

10

Les autres réponses montrent plusieurs solutions correctes pour résoudre ce problème spécifique d'une manière propre et concise. Voici une solution qui est non recommandée pour ce problème spécifique, mais illustre une autre technique: Dans les fonctions constexpr, vous pouvez utiliser les boucles plain for et la logique simple afin de calculer les résultats à la compilation temps. Cela permet de se débarrasser de la récursivité et de la tentative de spécialisation partielle du modèle du code 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>(), "");

Nécessite au moins C++ 14.

2
Julius