Comment utiliser les opérateurs de comparaison sur les variantes avec types contenus?

J’utilise beaucoup la variante dans mon code et je dois faire des comparaisons avec le contenu de certains endroits pour tester le contenu de la variante.

Par exemple:

if(equals(aVariant, 0)){ //Something } else { //Something else } 

avec cette fonction de modèle simple, j’ai écrit à cet effet:

 template inline bool equals(V& variant, T value){ return boost::get(&variant) && boost::get(variant) == value; } 

Cela fonctionne bien, mais le code commence à être difficile à lire. Je préfère utiliser des opérateurs de comparaison comme celui-ci:

 if(aVariant == 0){ //Something } else { //Something else } 

Mais je n’ai pas pu arriver avec une implémentation valide de l’opérateur. Le problème est que l’opérateur == a déjà été implémenté dans la variante pour échouer au moment de la compilation …

Est-ce que quelqu’un connaît un moyen de le mettre en œuvre de toute façon? Ou un moyen de désactiver cette limitation? Même si je dois implémenter une version pour chaque type possible contenu dans la variante, ce n’est pas un problème.

Merci

Comme indiqué, je pense que le moyen le plus simple de résoudre ce problème serait de renforcer la mise en œuvre de boost::variant<> avec une stratégie d’opérateur (par opérateur, en réalité) permettant aux clients de remplacer le comportement pour des utilisations externes. ( Évidemment, cela représente beaucoup de travail de programmation générique ).

J’ai implémenté une solution de contournement. Cela vous permet d’implémenter des opérateurs personnalisés pour les variantes, même si ceux-ci sont implémentés dans boost/variant.hpp .

Mon cerveau devait utiliser BOOST_STRONG_TYPEDEF .

L’idée est de rompre la résolution de surcharge (ou au moins de faire de notre résolution personnalisée la résolution préférée) en faisant en sorte que nos variantes soient d’un type réel différent et vous ne pouvez pas accéder à un “espace de noms démilitarisé” (la barrière) car les déclarations en conflit résident dans l’espace de noms de classe lui-même , mais vous pouvez les empêcher de s’appliquer à votre type “leurre”.

Hélas, cela ne fonctionnera pas très bien pour l’ operator< et la famille, car boost strong-typedef travaille dur pour préserver la sémantique de commande totale (faible) avec le type 'base'. En anglais normal: les typedefs forts définissent également l' operator< ( délégation à l'implémentation du type de base ).

Ne vous inquiétez pas, nous pouvons faire un CUSTOM_STRONG_TYPEDEF et être sur notre joyeux chemin. Examinez les scénarios de test dans main pour la preuve de concept (résultat ci-dessous).

En raison des interactions intéressantes décrites, j'ai choisi l' operator< pour cette démonstration, mais je suppose que rien ne vous empêche d'obtenir un operator== personnalisé operator== pour vos types de variante.

 #include  #include  #include  #include  ///////////////////////////////////////////////////// // copied and reduced from boost/strong_typedef.hpp #define CUSTOM_STRONG_TYPEDEF(T, D) \ struct D \ /*: boost::totally_ordered1< D */ \ /*, boost::totally_ordered2< D, T */ \ /*> > */ \ { \ T t; \ explicit D(const T t_) : t(t_) {}; \ D(){}; \ D(const D & t_) : t(t_.t){} \ D & operator=(const D & rhs) { t = rhs.t; return *this;} \ D & operator=(const T & rhs) { t = rhs; return *this;} \ operator const T & () const {return t; } \ operator T & () { return t; } \ /*bool operator==(const D & rhs) const { return t == rhs.t; } */\ /*bool operator<(const D & rhs) const { return t < rhs.t; } */\ }; namespace detail { typedef boost::variant variant_t; struct less_visitor : boost::static_visitor { bool operator()(const std::ssortingng& a, int b) const { return boost::lexical_cast(a) < b; } bool operator()(int a, const std::string& b) const { return a < boost::lexical_cast(b); } template  bool operator()(const T& a, const T& b) const { return a < b; } }; struct variant_less { less_visitor _helper; bool operator()(const variant_t& a, const variant_t& b) const { return boost::apply_visitor(_helper, a, b); } }; } CUSTOM_STRONG_TYPEDEF(detail::variant_t, custom_vt); namespace { bool operator<(const custom_vt& a, const custom_vt& b) { return detail::variant_less()(a, b); } std::ostream& operator<<(std::ostream& os, const custom_vt& v) { return os << (const detail::variant_t&)v; } } int main() { const detail::variant_t I(43), S("42"); const custom_vt i(I), s(S); // regression test (compare to boost behaviour) std::cout << "boost: " << I << " < " << S << ": " << std::boolalpha << (I 

Sortie:

 boost: 43 < 42: true boost: 42 < 43: false clumsy: 43 < 42: false clumsy: 42 < 43: true clumsy: 43 < 42: false clumsy: 42 < 43: true custom: 43 < 42: false custom: 42 < 43: true 

Maintenant, bien sûr, il peut y avoir des interactions malheureuses si vous souhaitez transmettre votre custom_vt aux API de bibliothèque qui utilisent TMP pour agir sur des variantes. Cependant, en raison des conversions sans douleur entre les deux, vous devriez pouvoir vous "frayer un chemin" en utilisant detail :: variant_t aux moments appropriés.

C'est le prix que vous devez payer pour obtenir la commodité syntaxique sur le site de l'appel.