1
votes

Pourquoi type_traits est-il implémenté à l'aide de structs

La plupart des traits de type sont créés à l'aide de la spécialisation partielle (ou complète) des structures et des modèles. Par exemple, std :: is_same est implémenté comme

bool are_equal = std::is_same<T, U>;

L'utilisation serait

template<typename>
inline constexpr bool is_same = false;
template<typename T>
inline constexpr bool is_same<T, T> = true;

ou p >

.. = std::is_same_v<T, U>;

qui sont tous les deux un peu moche. Au début, je pensais que c'était la seule solution, puis j'ai découvert que les variables peuvent également être partiellement spécialisées (alors que les fonctions ne le peuvent pas). Les traits de transformation de type ne peuvent pas (évidemment) être des variables, mais pour les traits "information", pourquoi ce ne serait pas mieux qu'une structure?

bool are_equal = std::is_same<T, U>::value;

Et puis

template<typename>
struct is_same : false_type {};
template<typename T>
struct is_same<T, T> : true_type {}; // partial specialization


0 commentaires

3 Réponses :


2
votes

La raison est purement historique. La plupart des traits de type ont d'abord été ajoutés à la bibliothèque, en C ++ 11. Des modèles de variables ont été ajoutés plus tard dans C ++ 14, et ce point de modification de l'implémentation était irréalisable, car cela est trop incompatible vers l'arrière. Des variables avec un suffixe _v ont donc été ajoutées, et c'est devenu la convention.

Les nouveaux traits sont ajoutés de la même manière car il est important d'avoir de la cohérence dans n'importe quelle bibliothèque, sans parler de la bibliothèque standard.


1 commentaires

Les variables inline étaient également une fonctionnalité C ++ 17, c'est pourquoi les modèles _v n'apparaissaient pas jusque-là aussi.



0
votes

Parfois, vous voulez un type, pas seulement une valeur bool sous-jacente. Un exemple simple est fourni par la technique de répartition des balises:

void foo(std::true_type)  { ... }
void foo(std::false_type) { ... }

foo(std::is_same<T, S>{});

Si std :: is_same était juste un modèle de variable bool , ce code serait pas être aussi élégant.


6 commentaires

Je ne sais pas pourquoi ce code est particulièrement élégant. Que se passe-t-il dans ces fonctions qui ne peut pas être fait comme conditionnel dans la fonction? Quel est l'intérêt de distribuer des balises autour d'une valeur booléenne ? S'il s'agit de trucs constexpr , vous pouvez facilement passer le booléen comme paramètre de modèle non-type et utiliser if constexpr .


@NicolBolas, si ces deux foo faisaient des choses différentes, je préférerais les avoir comme deux fonctions distinctes plutôt que d'avoir deux branches pour if constexpr dans une seule .


S'ils font vraiment des choses si distinctes, ne devraient-ils pas avoir des noms distincts? Pourquoi tenteriez-vous d'invoquer l'un ou l'autre en fonction d'une différence de paramètres? Autrement dit, du point de vue du lecteur de foo (is_same {}) , je m'attendrais à ce qu'un foo soit exécuté, quoi que cela signifie . Pas probablement un foo et peut-être un non-foo .


@NicolBolas, ils peuvent faire la même chose différemment, comme std :: distance fait la même chose pour l'accès aléatoire et transmettre les itérateurs de manière complètement différente.


C'est une question d'interface ou d'implémentation. Vous implémenteriez distance de différentes manières en fonction des propriétés du type d'itérateur qu'il utilise. Mais l ' interface pour la distance ne doit pas exposer ce détail à l'utilisateur. La distribution étiquetée dans le cadre de l'interface principale du type expose un détail d'implémentation à l'interface.


@NicolBolas, je suis totalement d'accord. Le code d'implémentation de bibliothèque standard Inside de ce type est omniprésent. Et je doute que cela devienne plus élégant s'il était réécrit en utilisant des branches if constexpr au lieu de l'envoi de balises.



0
votes

Notez que les paramètres de type et non-type ne sont pas interchangeables. Vous ne pouvez pas écrire un modèle unaire qui accepte les deux. Si vous deviez créer une algèbre de calcul complète à la compilation, vous devrez choisir un type de paramètre et faire en sorte que tous vos modèles n'acceptent que cela.


0 commentaires