it-swarm.com.de

Sind C++ - Enums signiert oder nicht signiert?

Sind C++ - Enums signiert oder nicht signiert? Und durch die Erweiterung ist es sicher, eine Eingabe zu überprüfen, indem überprüft wird, dass es <= Ihr maximaler Wert ist, und wegzulassen> = Ihr minimaler Wert (vorausgesetzt, Sie haben bei 0 begonnen und um 1 erhöht).

97
Matt

Sie sollten sich nicht auf eine bestimmte Darstellung verlassen. Lesen Sie den folgenden link . Außerdem sagt der Standard, dass es implementierungsdefiniert ist, welcher ganzzahlige Typ als zugrunde liegender Typ für eine Enumeration verwendet wird, mit der Ausnahme, dass er nicht größer als int sein sollte, es sei denn, ein Wert kann nicht in int oder ein vorzeichenloses int passen.

Kurz gesagt: Sie können sich nicht darauf verlassen, dass eine Enumeration signiert oder unsigniert ist.

60
zvrba

Gehen wir zur Quelle. Das sagt das Dokument C++ 03 (ISO/IEC 14882: 2003) in 7.2-5 (Enumeration-Deklarationen):

Der zugrunde liegende Typ einer Aufzählung ist ein ganzzahliger Typ, der .__ darstellen kann. Alle in .__ definierten Enumeratorwerte. die Aufzählung. Es ist implementierungsdefiniert welches Integral type wird als zugrundeliegender Typ verwendet für eine Aufzählung, außer dass die zugrunde liegender Typ darf nicht größer sein als int, es sei denn, der Wert eines Enumerator passt nicht in ein int oder unsigned int.

Kurz gesagt, Ihr Compiler kann wählen (wenn Sie negative Werte für einige Ihrer Werte für die Anzahl der Werte haben, wird dies natürlich signiert).

97
Michael Burr

Sie sollten sich nicht darauf verlassen, dass sie signiert oder unsigniert sind. Wenn Sie sie explizit signiert oder unsigniert machen möchten, können Sie Folgendes verwenden:

enum X : signed int { ... };    // signed enum
enum Y : unsigned int { ... };  // unsigned enum
21
Adam Rosenfield

Sie sollten sich nicht darauf verlassen, dass es signiert oder unsigniert ist. Gemäß dem Standard wird implementierungsdefiniert, welcher ganzzahlige Typ als zugrunde liegender Typ für eine Aufzählung verwendet wird. In den meisten Implementierungen ist es jedoch eine vorzeichenbehaftete Ganzzahl.

In C++ werden 0x stark typisierte Aufzählungen hinzugefügt, wodurch Sie den Typ einer Aufzählung angeben können, z.

enum X : signed int { ... };    // signed enum
enum Y : unsigned int { ... };  // unsigned enum

Einige einfache Überprüfungen können jedoch auch jetzt durch Verwendung der Aufzählung als Variable oder als Parametertyp wie folgt erreicht werden:

enum Fruit { Apple, Banana };

enum Fruit fruitVariable = Banana;  // Okay, Banana is a member of the Fruit enum
fruitVariable = 1;  // Error, 1 is not a member of enum Fruit
                    // even though it has the same value as banana.
14
Matt

Sogar einige alte Antworten erhielten 44 Stimmen, ich stimme allen nicht zu. Kurz gesagt, ich denke nicht, dass wir uns um den underlying type der Aufzählung kümmern sollten.

Zunächst ist C++ 03 Enum-Typ ein eigener Typ, der kein Zeichenkonzept hat. Seit C++ 03 Standard dcl.enum

7.2 Enumeration declarations 
5 Each enumeration defines a type that is different from all other types....

Wenn wir also über das Vorzeichen eines Aufzählungstyps sprechen, beispielsweise beim Vergleich von 2 Enum-Operanden mit dem <-Operator, sprechen wir eigentlich davon, dass der Enummentyp implizit in einen ganzzahligen Typ umgewandelt wird. Es ist das Zeichen dieses integralen Typs, auf den es ankommt. Bei der Konvertierung von Enum in Integral gilt diese Anweisung:

9 The value of an enumerator or an object of an enumeration type is converted to an integer by integral promotion (4.5).

Und offensichtlich hat die zugrunde liegende Art der Aufzählung nichts mit der Integral Promotion zu tun. Da der Standard Integral Promotion wie folgt definiert:

4.5 Integral promotions conv.prom
.. An rvalue of an enumeration type (7.2) can be converted to an rvalue of the first of the following types that can represent all the values of the enumeration
(i.e. the values in the range bmin to bmax as described in 7.2: int, unsigned int, long, or unsigned long.

Ob ein Enummentyp signed int oder unsigned int wird, hängt also davon ab, ob signed int alle Werte der definierten Enumeratoren enthalten kann, nicht den zugrunde liegenden Typ der Enumeration.

Siehe verwandte question Zeichen des C++ - Enummentyps nach Konvertierung in Integraltyp falsch

5
JavaMan

Der Compiler kann entscheiden, ob Aufzählungen signiert oder nicht signiert sind.

Eine weitere Methode zur Validierung von Enums besteht darin, das Enum selbst als Variablentyp zu verwenden. Zum Beispiel:

enum Fruit
{
    Apple = 0,
    Banana,
    Pineapple,
    Orange,
    Kumquat
};

enum Fruit fruitVariable = Banana;  // Okay, Banana is a member of the Fruit enum
fruitVariable = 1;  // Error, 1 is not a member of enum Fruit even though it has the same value as banana.
5
Cristián Romo

Zusätzlich zu dem, was andere bereits über signiert/unsigniert sagten, sagt der Standard über den Bereich eines aufgezählten Typs:

7.2 (6): "Für eine Aufzählung, bei der e(min) der kleinste Enumerator und e(max) der größte ist, sind die Werte der Aufzählung die Werte des zugrunde liegenden Werts Geben Sie im Bereich b(min) bis b (max) ein, wobei b(min) und b(max) jeweils der kleinste und der größte ist Werte des kleinsten Bitfelds, das gespeichert werden kann e(min) und e (max). Es ist möglich, eine Aufzählung zu definieren, deren Werte von keinem der Enumeratoren definiert werden. "

Also zum Beispiel:

enum { A = 1, B = 4};

definiert einen Aufzählungstyp, bei dem e(min) 1 ist und e(max) 4 ist. Wenn der zugrunde liegende Typ vorzeichenbehaftet ist, hat das kleinste erforderliche Bitfeld 4 Bits und wenn Ints in Ihrer Implementierung sind Zweierkomplementierungen. Der gültige Bereich der Enumeration ist -8 bis 7. Wenn der zugrunde liegende Typ unsigniert ist, hat er 3 Bits und der Bereich ist 0 bis 7. Überprüfen Sie in der Dokumentation Ihres Compilers, ob Sie dies möchten (z Wenn Sie andere Integralwerte als Enumeratoren in den Aufzählungstyp umwandeln möchten, müssen Sie wissen, ob der Wert im Bereich der Aufzählung liegt oder nicht - falls nicht der resultierende Aufzählungswert nicht angegeben ist.

Ob diese Werte gültige Eingaben für Ihre Funktion sind, hängt möglicherweise davon ab, ob es sich um gültige Werte des aufgezählten Typs handelt. Ihr Überprüfungscode ist wahrscheinlich eher um den ersteren als den letzteren besorgt. In diesem Beispiel sollten Sie also mindestens> = A und <= B prüfen.

4
Steve Jessop

In der Zukunft sind mit C++ 0x stark typisierte Aufzählungen verfügbar und bieten mehrere Vorteile (wie Typsicherheit, explizite zugrunde liegende Typen oder explizite Definition). Damit können Sie sicherer auf das Zeichen des Typs achten.

4
Kris Kumler

Überprüfen Sie es mit std::is_signed<std::underlying_type +, um die Standardwerte für int aufzurufen.

https://en.cppreference.com/w/cpp/language/enum implies:

main.cpp

#include <cassert>
#include <iostream>
#include <type_traits>

enum Unscoped {};
enum class ScopedDefault {};
enum class ScopedExplicit : long {};

int main() {
    // Implementation defined, let's find out.
    std::cout << std::is_signed<std::underlying_type<Unscoped>>() << std::endl;

    // Guaranteed. Scoped defaults to int.
    assert((std::is_same<std::underlying_type<ScopedDefault>::type, int>()));

    // Guaranteed. We set it ourselves.
    assert((std::is_same<std::underlying_type<ScopedExplicit>::type, long>()));
}

GitHub Upstream .

Kompilieren und ausführen:

g++ -std=c++17 -Wall -Wextra -pedantic-errors -o main main.cpp
./main

Ausgabe:

0

Getestet auf Ubuntu 16.04, GCC 6.4.0.