Je pense que vous trouverez la meilleure réponse possible à votre question dans ce livre d'Andrei Alexandrescu . Je vais essayer de vous en donner un bref aperçu. J'espère que cela vous aidera.
A classe de traits est une classe qui est généralement destinée à être une métafonction associant des types à d'autres types ou à des valeurs constantes pour fournir une caractérisation de ces types. En d'autres termes, il s'agit d'un moyen de modéliser les propriétés des types . Le mécanisme exploite normalement les modèles et la spécialisation des modèles pour définir l'association :
template<typename T>
struct my_trait
{
typedef T& reference_type;
static const bool isReference = false;
// ... (possibly more properties here)
};
template<>
struct my_trait<T&>
{
typedef T& reference_type;
static const bool isReference = true;
// ... (possibly more properties here)
};
La métafonction trait my_trait<>
ci-dessus associe le type de référence T&
et la valeur booléenne constante false
à tous les types T
qui sont no eux-mêmes des références ; d'autre part, il associe le type de référence T&
et la valeur booléenne constante true
à tous les types T
que sont références.
Ainsi, par exemple :
int -> reference_type = int&
isReference = false
int& -> reference_type = int&
isReference = true
En code, nous pourrions affirmer ce qui précède comme suit (les quatre lignes ci-dessous compileront, ce qui signifie que la condition exprimée dans le premier argument à static_assert()
est satisfaite) :
static_assert(!(my_trait<int>::isReference), "Error!");
static_assert( my_trait<int&>::isReference, "Error!");
static_assert(
std::is_same<typename my_trait<int>::reference_type, int&>::value,
"Error!"
);
static_assert(
std::is_same<typename my_trait<int&>::reference_type, int&>::value,
"Err!"
);
Ici, vous pouvez voir que j'ai utilisé le standard std::is_same<>
qui est lui-même une méta-fonction acceptant deux plutôt qu'un seul argument de type. Les choses peuvent devenir arbitrairement compliquées ici.
Bien que std::is_same<>
fait partie de la type_traits
certains considèrent qu'un modèle de classe n'est une classe à traits de type que s'il agit comme un méta-prédicat (acceptant donc un paramètre de modèle). À ma connaissance, cependant, la terminologie n'est pas clairement définie.
Pour un exemple d'utilisation d'une classe de traits dans la bibliothèque standard C++, regardez comment sont conçues les bibliothèques Input/Output et String.
A politique est quelque chose de légèrement différent (en fait, assez différent). Il s'agit normalement d'une classe qui spécifie le comportement d'une autre classe générique concernant certaines opérations qui peuvent être réalisées de différentes manières (et dont la mise en œuvre est, par conséquent, laissée à la classe de politique).
Par exemple, une classe générique de pointeurs intelligents pourrait être conçue comme une classe modèle acceptant une politique comme paramètre de modèle pour décider de la manière de gérer le comptage des références. Il ne s'agit que d'un exemple hypothétique, excessivement simpliste et illustratif, alors essayez de faire abstraction de ce code concret et de vous concentrer sur l'objectif de la classe. mécanisme .
Cela permettrait au concepteur du pointeur intelligent de ne pas prendre d'engagement codé en dur quant à savoir si les modifications du compteur de référence doivent être effectuées de manière sûre pour les threads :
template<typename T, typename P>
class smart_ptr : protected P
{
public:
// ...
smart_ptr(smart_ptr const& sp)
:
p(sp.p),
refcount(sp.refcount)
{
P::add_ref(refcount);
}
// ...
private:
T* p;
int* refcount;
};
Dans un contexte multithread, un client pourrait utiliser une instanciation du modèle de pointeur intelligent avec une politique qui réalise des incréments et des décréments du compteur de référence à l'abri des threads (plateforme Windows supposée ici) :
class mt_refcount_policy
{
protected:
add_ref(int* refcount) { ::InterlockedIncrement(refcount); }
release(int* refcount) { ::InterlockedDecrement(refcount); }
};
template<typename T>
using my_smart_ptr = smart_ptr<T, mt_refcount_policy>;
Dans un environnement monofilaire, par contre, un client pourrait instancier le modèle de pointeur intelligent avec une classe de politique qui augmente et diminue simplement la valeur du compteur :
class st_refcount_policy
{
protected:
add_ref(int* refcount) { (*refcount)++; }
release(int* refcount) { (*refcount)--; }
};
template<typename T>
using my_smart_ptr = smart_ptr<T, st_refcount_policy>;
De cette façon, le concepteur de la bibliothèque a fourni une solution flexible, capable d'offrir le meilleur compromis entre performance et sécurité ( "Vous ne payez pas pour ce que vous n'utilisez pas". ).
5 votes
Un trait tend à être une propriété qui est dérivée d'une classe, plutôt que de lui être donnée.
2 votes
Si vous n'êtes pas sûr de vous, vous trouverez de nombreuses informations sur les caractéristiques sur Internet : Une introduction aux Traits C++ , Comment fonctionnent les classes de traits ? , Traits : une nouvelle technique de gabarit utile
0 votes
Donc, ce que j'ai est en fait une politique et non un trait ?
1 votes
Oui, cela ressemble plus à une politique
0 votes
@Tony, merci pour les liens car ils constituent une bonne introduction aux traits.
0 votes
C'est un bon candidat pour le tag c++FAQ.