std:: enable_if
|
Défini dans l'en-tête
<type_traits>
|
||
|
template
<
bool
B,
class
T
=
void
>
struct enable_if ; |
(depuis C++11) | |
Si
B
est
true
,
std::enable_if
a un typedef membre public
type
, égal à
T
; sinon, il n'y a pas de typedef membre.
Cette métafonction est un moyen pratique de tirer parti de SFINAE avant les concepts de C++20, en particulier pour supprimer conditionnellement des fonctions de l' ensemble de candidats basé sur des traits de type, permettant des surcharges de fonctions ou des spécialisations distinctes basées sur ces différents traits de type.
std::enable_if
peut être utilisé sous de nombreuses formes, notamment :
- comme argument de fonction supplémentaire (non applicable à la plupart des surcharges d'opérateurs),
- comme type de retour (non applicable aux constructeurs et destructeurs),
- comme paramètre de modèle de classe ou de modèle de fonction.
Si le programme ajoute des spécialisations pour
std::enable_if
, le comportement est indéfini.
Table des matières |
Types membres
| Type | Définition |
type
|
soit
T
soit aucun membre, selon la valeur de
B
|
Types auxiliaires
|
template
<
bool
B,
class
T
=
void
>
using enable_if_t = typename enable_if < B,T > :: type ; |
(depuis C++14) | |
Implémentation possible
template<bool B, class T = void> struct enable_if {}; template<class T> struct enable_if<true, T> { typedef T type; }; |
` n'a pas été traduit conformément aux instructions, car il contient des termes spécifiques au C++ et du code source qui doit rester intact. La structure HTML et les attributs ont également été préservés sans modification.
Notes
Une erreur courante est de déclarer deux modèles de fonction qui ne diffèrent que par leurs arguments de modèle par défaut. Cela ne fonctionne pas car les déclarations sont traitées comme des redéclarations du même modèle de fonction (les arguments de modèle par défaut ne sont pas pris en compte dans l'équivalence des modèles de fonction ).
/* INCORRECT */ struct T { enum { int_t, float_t } type; template<typename Integer, typename = std::enable_if_t<std::is_integral<Integer>::value>> T(Integer) : type(int_t) {} template<typename Floating, typename = std::enable_if_t<std::is_floating_point<Floating>::value>> T(Floating) : type(float_t) {} // erreur : traité comme une redéfinition }; /* CORRECT */ struct T { enum { int_t, float_t } type; template<typename Integer, std::enable_if_t<std::is_integral<Integer>::value, bool> = true> T(Integer) : type(int_t) {} template<typename Floating, std::enable_if_t<std::is_floating_point<Floating>::value, bool> = true> T(Floating) : type(float_t) {} // OK };
Il convient de prêter attention lors de l'utilisation de
enable_if
dans le type d'un paramètre de modèle constant d'un modèle de fonction de portée d'espace de noms. Certaines spécifications ABI comme l'ABI Itanium n'incluent pas les parties dépendantes de l'instanciation des paramètres de modèle constants dans le mangling, ce qui signifie que les spécialisations de deux modèles de fonction distincts pourraient aboutir au même nom manglé et être liées ensemble par erreur. Par exemple :
// première unité de traduction struct X { enum { value1 = true, value2 = true }; }; template<class T, std::enable_if_t<T::value1, int> = 0> void func() {} // #1 template void func<X>(); // #2 // deuxième unité de traduction struct X { enum { value1 = true, value2 = true }; }; template<class T, std::enable_if_t<T::value2, int> = 0> void func() {} // #3 template void func<X>(); // #4
Les modèles de fonction #1 et #3 ont des signatures différentes et sont des modèles distincts. Néanmoins, #2 et #4, bien qu'étant des instanciations de différents modèles de fonction, ont le même nom manglé
dans l'ABI C++ Itanium
(
_Z4funcI1XLi0EEvv
), ce qui signifie que l'éditeur de liens les considérera erronément comme étant la même entité.
Exemple
#include <iostream> #include <new> #include <string> #include <type_traits> namespace detail { void* voidify(const volatile void* ptr) noexcept { return const_cast<void*>(ptr); } } // #1, activé via le type de retour template<class T> typename std::enable_if<std::is_trivially_default_constructible<T>::value>::type construct(T*) { std::cout << "construction par défaut de T trivialement constructible par défaut\n"; } // idem ci-dessus template<class T> typename std::enable_if<!std::is_trivially_default_constructible<T>::value>::type construct(T* p) { std::cout << "construction par défaut d'un T non trivialement constructible par défaut\n"; ::new(detail::voidify(p)) T; } // #2 template<class T, class... Args> std::enable_if_t<std::is_constructible<T, Args&&...>::value> // Utilisation d'un type auxiliaire construct(T* p, Args&&... args) { std::cout << "construction de T avec l'opération\n"; ::new(detail::voidify(p)) T(static_cast<Args&&>(args)...); } // #3, activé via un paramètre template<class T> void destroy( T*, typename std::enable_if< std::is_trivially_destructible<T>::value >::type* = 0) { std::cout << "détruisant T trivialement destructible\n"; } // #4, activé via un paramètre template constant template<class T, typename std::enable_if< !std::is_trivially_destructible<T>{} && (std::is_class<T>{} || std::is_union<T>{}), bool>::type = true> void destroy(T* t) { std::cout << "détruisant un T non trivialement destructible\n"; t->~T(); } // #5, activé via un paramètre de template de type template<class T, typename = std::enable_if_t<std::is_array<T>::value>> void destroy(T* t) // note: function signature is unmodified { for (std::size_t i = 0; i < std::extent<T>::value; ++i) destroy((*t)[i]); } /* template<class T, typename = std::enable_if_t<std::is_void<T>::value>> void destroy(T* t) {} // erreur: a la même signature que #5 */ // la spécialisation partielle de A est activée via un paramètre de template template<class T, class Enable = void> class A {}; // modèle principal template<class T> class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {}; // spécialisation pour les types à virgule flottante int main() { union { int i; char s[sizeof(std::string)]; } u; construct(reinterpret_cast<int*>(&u)); destroy(reinterpret_cast<int*>(&u)); construct(reinterpret_cast<std::string*>(&u), "Bonjour"); destroy(reinterpret_cast<std::string*>(&u)); A<int>{}; // OK: correspond au modèle principal A<double>{}; // OK: correspond à la spécialisation partielle }
Sortie :
construction par défaut de T trivialement constructible par défaut destruction de T trivialement destructible construction de T avec opération destruction de T non trivialement destructible
Voir aussi
|
(C++17)
|
Modèle d'alias variadique void
(modèle d'alias) |