web-dev-qa-db-fra.com

Comment surcharger | = opérateur sur un domaine couvert?

Comment surcharger l'opérateur |= sur une enumfortement typée (en C++ 11, GCC)?

Je veux tester, définir et effacer des bits sur des énumérations fortement typées. Pourquoi fortement typé? Parce que mes livres disent que c'est une bonne pratique. Mais cela signifie que je dois static_cast<int> partout. Pour éviter cela, je surcharge les opérateurs | et &, mais je ne vois pas comment surcharger l'opérateur |=sur un enum. Pour une classe, vous devez simplement mettre la définition de l'opérateur dans la classe , mais pour des énumérations qui ne semblent pas fonctionner syntaxiquement.

Voici ce que j'ai jusqu'à présent:

enum class NumericType
{
    None                    = 0,

    PadWithZero             = 0x01,
    NegativeSign            = 0x02,
    PositiveSign            = 0x04,
    SpacePrefix             = 0x08
};

inline NumericType operator |(NumericType a, NumericType b)
{
    return static_cast<NumericType>(static_cast<int>(a) | static_cast<int>(b));
}

inline NumericType operator &(NumericType a, NumericType b)
{
    return static_cast<NumericType>(static_cast<int>(a) & static_cast<int>(b));
}

La raison pour laquelle je fais cela: c’est ainsi que cela fonctionne dans un C # fortement typé: enum il existe juste une structure avec un champ de son type sous-jacent, et un ensemble de constantes définies dessus. Mais il peut avoir n'importe quelle valeur entière qui rentre dans le champ caché de l'énum.

Et il semble que les énumérations C++ fonctionnent exactement de la même manière. Dans les deux langues, les casts doivent aller d’enum à int ou inversement. Cependant, en C #, les opérateurs au niveau du bit sont surchargés par défaut, mais pas en C++.

24
inline NumericType& operator |=(NumericType& a, NumericType b)
{
    return a= a |b;
}

Cela marche? Compiler et exécuter: (Ideone)

#include <iostream>
using namespace std;

enum class NumericType
{
    None                    = 0,

    PadWithZero             = 0x01,
    NegativeSign            = 0x02,
    PositiveSign            = 0x04,
    SpacePrefix             = 0x08
};

inline NumericType operator |(NumericType a, NumericType b)
{
    return static_cast<NumericType>(static_cast<int>(a) | static_cast<int>(b));
}

inline NumericType operator &(NumericType a, NumericType b)
{
    return static_cast<NumericType>(static_cast<int>(a) & static_cast<int>(b));
}

inline NumericType& operator |=(NumericType& a, NumericType b)
{
    return a= a |b;
}

int main() {
    // your code goes here
    NumericType a=NumericType::PadWithZero;
    a|=NumericType::NegativeSign;
    cout << static_cast<int>(a) ;
    return 0;
}

imprimer 3.

26
qPCR4vir

Cela semble fonctionner pour moi:

NumericType operator |= (NumericType &a, NumericType b) {
    unsigned ai = static_cast<unsigned>(a);
    unsigned bi = static_cast<unsigned>(b);
    ai |= bi;
    return a = static_cast<NumericType>(ai);
}

Cependant, vous pouvez toujours envisager de définir une classe pour votre collection de enum bits:

class NumericTypeFlags {
    unsigned flags_;
public:
    NumericTypeFlags () : flags_(0) {}
    NumericTypeFlags (NumericType t) : flags_(static_cast<unsigned>(t)) {}
    //...define your "bitwise" test/set operations
};

Ensuite, changez vos opérateurs | et & pour renvoyer NumericTypeFlags à la place.

2
jxh

En combinant des valeurs distinctes pour créer de nouvelles valeurs non définies, vous contredisez totalement le paradigme du typage fort.

Il semble que vous définissiez des bits d'indicateur individuels complètement indépendants. Dans ce cas, il n’a aucun sens de combiner vos bits dans un type de données où une telle combinaison produit une valeur indéfinie.

Vous devez décider de la taille de vos données de drapeau (char, short, long, long long) et vous en servir. Vous pouvez toutefois utiliser des types spécifiques pour tester, définir et effacer des indicateurs:

typedef enum
{
    PadWithZero             = 0x01,
    NegativeSign            = 0x02,
    PositiveSign            = 0x04,
    SpacePrefix             = 0x08
} Flag;

typedef short Flags;

void SetFlag( Flags & flags, Flag f )
{
    flags |= static_cast<Flags>(f);
}

void ClearFlag( Flags & flags, Flag f )
{
    flags &= ~static_cast<Flags>(f);
}

bool TestFlag( const Flags flags, Flag f )
{
    return (flags & static_cast<Flags>)(f)) == static_cast<Flags>(f);
}

Ceci est très basique et convient lorsque chaque drapeau n’est qu’un seul bit. Pour les drapeaux masqués, c'est un peu plus complexe. Il existe des moyens d'encapsuler les indicateurs de bits dans une classe fortement typée, mais cela doit en valoir la peine. Dans votre cas, je ne suis pas convaincu que ce soit le cas.

0
paddy