std :: enable_if
std :: enable_if est un bon outil en c++, utilisant SFINAE pour traiter différents types différemment.
Pour résumer, std :: enable_if a quatre méthodes d'utilisation, qui sont divisées en valeur de retour, paramètre, paramètre de modèle de type et paramètre de modèle non type. Pour les deux derniers, les versions supérieures à c++ 11 sont requises.
Par exemple, si j'ai besoin de sortir un nombre, le type de modèle T doit être un type numérique. Examinons quatre utilisations.
valeur de retour
template<class T>
typename std::enable_if<std::is_integral<T>::value>::type
printIntegral(T t) {
std::cout << t;
}
Lorsque enable_if ne spécifie pas le deuxième paramètre de modèle, enable_if::type par défaut est void, il n'y a donc pas besoin de valeur de retour.
paramètre
template<class T>
void printIntegral(T t,
typename std::enable_if<std::is_integral<T>::value>::type* = 0) {
std::cout << t;
}
Utilisez 0 comme paramètre par défaut du pointeur de type T à déduire.
Cependant, les deux méthodes ci-dessus sont relativement anciennes, car C++11 n'autorisait pas les paramètres de modèle à avoir des valeurs par défaut, mais il est maintenant préférable d'utiliser les deux méthodes suivantes.
paramètres de modèle non typés
template<class T, typename std::enable_if<std::is_integral<T>::value,int>::type = 0>
void printIntegral(T t) {
std::cout << t;
}
paramètre de modèle de type
template<class T, typename = std::enable_if<std::is_integral<T>::value>>
void printIntegral(T&& t) {
std::cout << t;
}
Personnellement, je pense que la meilleure méthode est plus concise que les trois méthodes ci-dessus et qu'elle peut également gérer les situations lvalue et rvalue.
void_t
void_t est utilisé pour vérifier si l'expression SFINAE est valide au moment de la compilation, par exemple, pour vérifier si une certaine classe a une certaine fonction membre.
Par exemple, si nous voulons détecter si une classe a une fonction amusante, nous pouvons
class Foo {
public:
Foo() {
};
void fun() {
std::cout << "fun";
}
};
template<class T, class = void>
struct has_fun : std::false_type{
};
template<class T>
struct has_fun<T, void_t<decltype(std::declval<T>().fun())>> : std::true_type {
};
template<class T>
constexpr auto has_fun_t = has_fun<T>::value;
template<class T, std::enable_if<has_fun_t<T>,int>::type = 0>
void call(T t) {
t.fun();
}
template<class T, std::enable_if<!has_fun_t<T>, int>::type = 0>
void call(T t) {
std::cout << "no fun";
}
Le template principal de has_fun hérite de flase_type, et si le paramètre de template T a cette fonction, il est spécialisé comme has_fun héritant de true_type. La fonction d'appel est jugée par enable_if.
Ce serait plus simple si le standard 17 était supporté lors de la compilation, et si constexpr pouvait être utilisé pour écrire moins de code.