J'ai une classe avec un std::variant
dedans. Ce type std::variant
est uniquement autorisé à contenir une liste spécifique de types.
J'ai des fonctions de modèle qui permettent à l'utilisateur de la classe d'insérer différentes valeurs dans un std::unordered_map
, la carte contient les valeurs de ce type variant. En d'autres termes, l'utilisateur n'est autorisé à insérer des valeurs que si son type figure dans la liste spécifique des types. Cependant, je ne veux pas que l'utilisateur puisse définir cette liste de types eux-mêmes.
class GLCapabilities
{
public:
using VariantType = std::variant<GLint64>; // in future this would have other types
template <typename T>
std::enable_if_t<???> AddCapability(const GLenum parameterName)
{
if(m_capabilities.count(parameterName) == 0)
{
/*... get correct value of type T ... */
m_capabilities.insert(parameterName,value);
}
}
template<typename T>
std::enable_if_t<???,T> GetCapability(const GLenum parameterName) const
{
auto itr = m_capabilities.find(parameterName);
if(std::holds_alternative<T>(*itr))
return std::get<T>(*itr);
return T;
}
private:
std::unordered_map<GLenum,VariantType> m_capabilities;
};
Vous verrez ci-dessus où se trouve le ???
, comment puis-je vérifier? Une certaine combinaison de std::disjunction
avec std::is_same
?
Comme
std::enable_if<std::disjunction<std::is_same<T,/*Variant Types???*/>...>>
Pour que ce soit clair, je préférerais ne pas avoir à vérifier manuellement chaque type autorisé.
Edit: Je creuse en fait votre idée std::disjunction
et elle fonctionne parfaitement. Il vous suffit d'extraire la liste de types à l'aide de la spécialisation de modèles.
Mon désordre récursif de la vieille école devient tout simplement:
template<typename T, typename VARIANT_T>
struct isVariantMember;
template<typename T, typename... ALL_T>
struct isVariantMember<T, std::variant<ALL_T...>>
: public std::disjunction<std::is_same<T, ALL_T>...> {};
Réponse originale: Voici un modèle simple qui accomplit cela. Cela fonctionne en retournant false
pour les listes de types vides. Pour les listes non vides, il retourne true
si le premier type passe std::is_same<>
et s'invoque récursivement avec tout sauf le premier type.
#include <vector>
#include <Tuple>
#include <variant>
// Main lookup logic of looking up a type in a list.
template<typename T, typename... ALL_T>
struct isOneOf : public std::false_type {};
template<typename T, typename FRONT_T, typename... REST_T>
struct isOneOf<T, FRONT_T, REST_T...> : public
std::conditional<
std::is_same<T, FRONT_T>::value,
std::true_type,
isOneOf<T, REST_T...>
>::type {};
// Convenience wrapper for std::variant<>.
template<typename T, typename VARIANT_T>
struct isVariantMember;
template<typename T, typename... ALL_T>
struct isVariantMember<T, std::variant<ALL_T...>> : public isOneOf<T, ALL_T...> {};
// Example:
int main() {
using var_t = std::variant<int, float>;
bool x = isVariantMember<int, var_t>::value; // x == true
bool y = isVariantMember<double, var_t>::value; // y == false
return 0;
}
N.B. Assurez-vous de supprimer les qualificatifs cv et référence de T avant de l'invoquer (ou d'ajouter la suppression au modèle lui-même). Cela dépend de vos besoins, vraiment.
template <class T> struct type {};
template <class T> constexpr type<T> type_v{};
template <class T, class...Ts, template<class...> class Tp>
constexpr bool is_one_of(type<Tp<Ts...>>, type<T>) {
return (std::is_same_v<Ts, T> || ...);
}
Ensuite, utilisez is_one_of(type_v<VariantType>, type_v<T>)
dans le enable_if
.
Vous pouvez éviter d'utiliser std::enable_if_t
et utiliser à la place des expressions SFINAE classiques à decltype
comme celle de l'exemple suivant:
#include<variant>
#include<utility>
struct S {
using type = std::variant<int, double>;
template<typename U>
auto f()
-> decltype(std::declval<type>().emplace<U>(), void()) {
// that's ok
}
};
int main() {
S s;
s.f<int>();
//s.f<char>();
}
Si vous basculez le commentaire sur la dernière ligne, vous obtenez une erreur de temps de compilation pour char
qui n'est pas un type accepté par votre variante.
L'avantage de cette solution est que c'est simple et qu'il n'est pas nécessaire d'inclure type_traits
(d'accord, vous devez inclure utility
) ni d'utiliser une classe de support pour obtenir une valeur bool à tester.
Bien entendu, vous pouvez ajuster le type de retour en fonction de vos besoins pour chaque fonction.
Voyez-le en marche sur wandbox .
Sinon, si vous pouvez vous en tenir aux limitations de std::holds_alternative
(l'appel est mal formé si le type se compare plus d'une fois dans la liste des paramètres de la variante), notez qu'il s'agit d'une fonction constexpr
et qu'elle fait juste ce que vous voulez:
#include<type_traits>
#include<variant>
#include<utility>
struct S {
using type = std::variant<int, double>;
template<typename U>
std::enable_if_t<std::holds_alternative<U>(type{})>
f() {
// that's ok
}
};
int main() {
S s;
s.f<int>();
//s.f<char>();
}
Comme ci-dessus, basculez le commentaire et vous obtiendrez une erreur de compilation comme prévu.
Voyez-le en marche sur wandbox .
Puisque vous utilisez déjà C++ 17, les expressions de plis facilitent cela:
template <class T, class U> struct is_one_of;
template <class T, class... Ts>
struct is_one_of<T, std::variant<Ts...>>
: std::bool_constant<(std::is_same_v<T, Ts> || ...)>
{ };
Pour plus de lisibilité, vous pouvez ajouter un alias dans votre classe:
class GLCapabilities
{
public:
using VariantType = std::variant<GLint64>; // in future this would have other types
template <class T> using allowed = is_one_of<T, VariantType>;
template <typename T>
std::enable_if_t<allowed<T>{}> AddCapability(const GLenum parameterName)
{ ... }
};
Vous pouvez essayer d'utiliser SFINAE en construisant la variable VariantType
à partir du type T
.
template <typename T, typename = VariantType(std::declval<T>())>
void AddCapability(T const& t); // not sure how you want to use it.
Ou utilisez std::is_constructible<VariantType, T>
. Après tout, vous voudrez probablement savoir si vous pouvez affecter/initialiser à partir du type, et non si le type est en réalité un des types variant (ce qui est plus restrictif).