La magie de enable_if et void_t en c++

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.

Guess you like

Origin blog.csdn.net/ninesnow_c/article/details/122945888