Mon code peut-il mélanger des pommes et des oranges ?
Dans votre projet C, vous utilisez de nombreux "usings" pour clarifier le but recherché des variables , principalement pour les identifiants std::string comme PortalId ou CakeId. Votre approche actuelle permet ce qui suit :
using PortalId = std::string; using CakeId = std::string; PortalId portal_id("2"); CakeId cake_id("is a lie"); portal_id = cake_id; // OK
Mais cette flexibilité n'est pas souhaitable. Vous recherchez une vérification de type au moment de la compilation pour éviter de mélanger différents types d'objets tout en préservant les fonctionnalités de l'objet d'origine.
C peut-il répondre à cette demande ?
Oui, C fournit une solution pour votre dilemme. En utilisant C 17 et un codage intelligent, il est possible de créer un identifiant fortement typé qui empêche le mélange des types. Les résultats ressemblent à ceci :
SAFE_TYPEDEF(std::string, PortalId); SAFE_TYPEDEF(std::string, CakeId); int main() { PortalId portal_id("2"); CakeId cake_id("is a lie"); std::map<CakeId, PortalId> p_to_cake; // OK p_to_cake[cake_id] = portal_id; // OK p_to_cake[portal_id] = cake_id; // COMPILER ERROR portal_id = cake_id; // COMPILER ERROR portal_id = "1.0"; // COMPILER ERROR portal_id = PortalId("42"); // OK return 0; }
Dans ce code, les affectations entre différents types ne sont pas autorisées et l'ajout de types incompatibles aux conteneurs entraînera des erreurs de compilation.
Qu'est-ce que le secret ?
La magie réside dans la macro SAFE_TYPEDEF. Voici la version mise à jour qui répond à tous vos besoins :
#include <iostream> #include <string> #include <map> #include <unordered_map> // define some tags to create uniqueness struct portal_tag {}; struct cake_tag {}; // a string-like identifier that is typed on a tag type template<class Tag> struct string_id { using tag_type = Tag; // needs to be default-constuctable because of use in map[] below string_id(std::string s) : _value(std::move(s)) {} string_id() : _value() {} // provide access to the underlying string value const std::string& value() const { return _value; } private: std::string _value; // will only compare against same type of id. friend bool operator < (const string_id& l, const string_id& r) { return l._value < r._value; } friend bool operator == (const string_id& l, const string_id& r) { return l._value == r._value; } // and let's go ahead and provide expected free functions friend auto to_string(const string_id& r) -> const std::string& { return r._value; } friend auto operator << (std::ostream& os, const string_id& sid) -> std::ostream& { return os << sid.value(); } friend std::size_t hash_code(const string_id& sid) { std::size_t seed = typeid(tag_type).hash_code(); seed ^= std::hash<std::string>()(sid._value); return seed; } }; // let's make it hashable namespace std { template<class Tag> struct hash<string_id<Tag>> { using argument_type = string_id<Tag>; using result_type = std::size_t; result_type operator()(const argument_type& arg) const { return hash_code(arg); } }; } // create some type aliases for ease of use using PortalId = string_id<portal_tag>; using CakeId = string_id<cake_tag>; using namespace std;
Ce code définit une classe string_id qui sert d'identifiant fortement typé. Il est étiqueté avec portal_tag ou cake_tag pour garantir son unicité. La classe surcharge également les opérateurs comme <, "==" et les fonctions gratuites telles que to_string, << et hash_code.
Avec cette implémentation, vous pouvez appliquer un typage fort et empêcher le mélange des types dans votre code. Il couvre tous les scénarios que vous avez mentionnés dans votre question et fournit une solution stable pour vos besoins.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!