wake-up-neo.com

Wie überlade ich | = Operator auf einer Bereichsaufzählung?

Wie kann ich den |=-Operator in einem stark typisierten (bereichsspezifischen) enum(in C++ 11, GCC) überladen?

Ich möchte Bits auf stark typisierten Aufzählungen testen, setzen und löschen. Warum stark getippt? Weil meine Bücher sagen, dass es eine gute Praxis ist. Das bedeutet aber, dass ich überall static_cast<int> muss. Um dies zu verhindern, überlade ich die Operatoren | und &, aber ich kann nicht herausfinden, wie ich den Operator |=in einer Aufzählung überlade. Für eine Klasse würden Sie einfach die Operatordefinition in die Klasse einfügen, aber für Aufzählungen scheint das nicht syntaktisch zu funktionieren.

Das habe ich bisher:

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));
}

Der Grund, warum ich das mache: So funktioniert es in stark typisiertem C #: Eine Enumeration besteht nur aus einer Struktur mit einem Feld des zugrunde liegenden Typs und einer Reihe darauf definierter Konstanten. Es kann jedoch einen beliebigen ganzzahligen Wert haben, der in das verborgene Feld der Aufzählung passt.

Und es scheint, dass C++ - Aufzählungen genauso funktionieren. In beiden Sprachen müssen die Casts von enum nach int oder umgekehrt wechseln. In C # sind die bitweisen Operatoren jedoch standardmäßig überladen und in C++ nicht.

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

Das funktioniert? Kompilieren und ausführen: (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;
}

druck 3.

26
qPCR4vir

Das scheint für mich zu funktionieren:

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);
}

Sie können jedoch trotzdem in Betracht ziehen, eine Klasse für Ihre Sammlung von enum-Bits zu definieren:

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

Ändern Sie dann Ihre |- und &-Operatoren, um stattdessen NumericTypeFlags zurückzugeben.

2
jxh

Indem Sie unterschiedliche Werte kombinieren, um neue, undefinierte Werte zu erstellen, widersprechen Sie dem Paradigma der starken Typisierung.

Es sieht so aus, als würden Sie einzelne Flag-Bits setzen, die völlig unabhängig sind. In diesem Fall ist es nicht sinnvoll, Ihre Bits zu einem Datentyp zu kombinieren, bei dem eine solche Kombination einen undefinierten Wert ergibt.

Sie sollten sich für die Größe Ihrer Flagdaten entscheiden (char, short, long, long long) und mit diesem rollen. Sie können jedoch bestimmte Typen zum Testen, Setzen und Löschen von Flags verwenden:

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);
}

Dies ist sehr einfach und in Ordnung, wenn jedes Flag nur ein einzelnes Bit enthält. Für maskierte Flaggen ist es etwas komplexer. Es gibt Möglichkeiten, Bitflags in eine stark typisierte Klasse zu kapseln, aber es muss sich wirklich lohnen. In Ihrem Fall bin ich nicht davon überzeugt.

0
paddy