Sind C ++ - Aufzählungen signiert oder nicht signiert? Und ist es sicher, eine Eingabe zu validieren, indem Sie überprüfen, ob sie <= Ihr Maximalwert ist, und> = Ihren Minimalwert weglassen (vorausgesetzt, Sie haben bei 0 begonnen und um 1 erhöht)?
Sind C ++ - Aufzählungen signiert oder nicht signiert? Und ist es sicher, eine Eingabe zu validieren, indem Sie überprüfen, ob sie <= Ihr Maximalwert ist, und> = Ihren Minimalwert weglassen (vorausgesetzt, Sie haben bei 0 begonnen und um 1 erhöht)?
Antworten:
Sie sollten sich nicht auf eine bestimmte Darstellung verlassen. Lesen Sie den folgenden Link . Außerdem besagt der Standard, dass implementierungsdefiniert ist, welcher Integraltyp als zugrunde liegender Typ für eine Aufzählung verwendet wird, mit der Ausnahme, dass er nicht größer als int sein darf, 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 Aufzählung signiert oder nicht signiert ist.
Gehen wir zur Quelle. Im Dokument zum C ++ 03-Standard (ISO / IEC 14882: 2003) heißt es in 7.2-5 (Aufzählungserklärungen):
Der zugrunde liegende Typ einer Aufzählung ist ein integraler Typ, der alle in der Aufzählung definierten Aufzählungswerte darstellen kann. Es ist implementierungsdefiniert, welcher Integraltyp als zugrunde liegender Typ für eine Aufzählung verwendet wird, mit der Ausnahme, dass der zugrunde liegende Typ nicht größer als int sein darf, es sei denn, der Wert eines Enumerators kann nicht in ein int oder ein vorzeichenloses int passen.
Kurz gesagt, Ihr Compiler kann wählen (wenn Sie für einige Ihrer Aufzählungswerte negative Zahlen haben, wird diese natürlich signiert).
Sie sollten sich nicht darauf verlassen, dass sie signiert oder nicht signiert sind. Wenn Sie sie explizit signiert oder nicht signiert machen möchten, können Sie Folgendes verwenden:
enum X : signed int { ... }; // signed enum
enum Y : unsigned int { ... }; // unsigned enum
Sie sollten sich nicht darauf verlassen, dass es signiert oder nicht signiert ist. Gemäß dem Standard ist implementierungsdefiniert, welcher Integraltyp als zugrunde liegender Typ für eine Aufzählung verwendet wird. In den meisten Implementierungen handelt es sich jedoch um eine vorzeichenbehaftete Ganzzahl.
In C ++ 0x werden stark typisierte Aufzählungen hinzugefügt, mit denen Sie den Typ einer Aufzählung angeben können, z.
enum X : signed int { ... }; // signed enum
enum Y : unsigned int { ... }; // unsigned enum
Selbst jetzt kann eine einfache Validierung erreicht werden, indem die Aufzählung als Variablen- oder Parametertyp wie folgt verwendet wird:
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.
Der Compiler kann entscheiden, ob Aufzählungen signiert oder nicht signiert sind.
Eine andere Methode zum Überprüfen von Aufzählungen besteht darin, die Aufzählung selbst als Variablentyp zu verwenden. Beispielsweise:
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.
Sogar einige alte Antworten haben 44 positive Stimmen erhalten, ich stimme allen nicht zu. Kurz gesagt, ich denke nicht, dass wir uns um das kümmern solltenunderlying type
die Aufzählung .
Zunächst einmal ist der C ++ 03-Aufzählungstyp ein eigenständiger Typ ohne Vorzeichenkonzept. Seit ab C ++ 03 Standarddcl.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 wenn wir zwei Aufzählungsoperanden mit dem <
Operator vergleichen, sprechen wir tatsächlich über die implizite Konvertierung des Aufzählungstyps in einen ganzzahligen Typ. Es ist das Zeichen dieses integralen Typs, das zählt . Und wenn Sie enum in einen integralen Typ konvertieren, gilt diese Aussage:
9 The value of an enumerator or an object of an enumeration type is converted to an integer by integral promotion (4.5).
Und anscheinend hat der zugrunde liegende Typ der Aufzählung nichts mit der integralen Förderung 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 Aufzählungstyp wird signed int
oder unsigned int
davon abhängt, ob er signed int
alle Werte der definierten Enumeratoren enthalten kann, nicht den zugrunde liegenden Typ der Aufzählung.
Siehe meine verwandte Frage Zeichen des C ++ - Aufzählungstyps nach Konvertierung in einen integralen Typ falsch
-Wsign-conversion
. Wir verwenden es, um unbeabsichtigte Fehler in unserem Code zu erkennen. Aber +1, um den Standard zu zitieren und darauf hinzuweisen, dass einer Aufzählung kein Typ ( signed
versus unsigned
) zugeordnet ist.
In Zukunft werden mit C ++ 0x stark typisierte Aufzählungen verfügbar sein und mehrere Vorteile haben (z. B. Typensicherheit, explizite zugrunde liegende Typen oder explizites Scoping). Damit können Sie sich des Zeichens des Typs besser sicher sein.
Zusätzlich zu dem, was andere bereits über signiert / nicht signiert gesagt haben, sagt der Standard Folgendes über den Bereich eines aufgezählten Typs:
7.2 (6): "Bei einer Aufzählung, bei der e (min) der kleinste Aufzähler und e (max) der größte ist, sind die Werte der Aufzählung die Werte des zugrunde liegenden Typs im Bereich b (min) bis b (max ), wobei b (min) und b (max) jeweils die kleinsten und größten Werte des kleinsten Bitfelds sind, in dem e (min) und e (max) gespeichert werden können. Es ist möglich, eine Aufzählung zu definieren, deren Werte nicht definiert sind von einem seiner Aufzähler. "
Also zum Beispiel:
enum { A = 1, B = 4};
definiert einen Aufzählungstyp, wobei e (min) 1 und e (max) 4 ist. Wenn der zugrunde liegende Typ int signiert ist, hat das kleinste erforderliche Bitfeld 4 Bits, und wenn Ints in Ihrer Implementierung Zweierkomplement sind, ist der gültige Bereich von Die Aufzählung ist -8 bis 7. Wenn der zugrunde liegende Typ ohne Vorzeichen ist, hat er 3 Bits und der Bereich liegt zwischen 0 und 7. Überprüfen Sie Ihre Compiler-Dokumentation, wenn Sie sich interessieren (zum Beispiel, wenn Sie andere Integralwerte als Enumeratoren in die umwandeln möchten Aufzählungstyp, dann müssen Sie wissen, ob der Wert im Bereich der Aufzählung liegt oder nicht - wenn nicht, ist der resultierende Aufzählungswert nicht angegeben).
Ob diese Werte eine gültige Eingabe für Ihre Funktion sind, hängt möglicherweise davon ab, ob es sich um gültige Werte vom Aufzählungstyp handelt. Ihr Prüfcode ist wahrscheinlich eher um den ersteren als um den letzteren besorgt und sollte daher in diesem Beispiel zumindest> = A und <= B prüfen.
Überprüfen Sie dies mit den std::is_signed<std::underlying_type
Aufzählungen mit + Gültigkeitsbereichint
https://en.cppreference.com/w/cpp/language/enum impliziert:
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>()));
}
Kompilieren und ausführen:
g++ -std=c++17 -Wall -Wextra -pedantic-errors -o main main.cpp
./main
Ausgabe:
0
Getestet unter Ubuntu 16.04, GCC 6.4.0.