Seite 1 von 1

enum class ohne enum Name verwenden

Verfasst: 07.10.2021, 13:55
von Jonathan
Sagen wir ich habe folgenden Code:

Code: Alles auswählen

enum class ZombieState {IDLE, WALK_AROUND};
ZombieState state;

switch(state)
{
case ZombieState::IDLE: // geht
	break;
case WALK_AROUND: // geht nicht :(
	break;
}
Wieso muss ich wenn ich eine Konstante aus dem enum verwende immer explizit dessen Namen angeben? Also, die Idee hinter enum class ist doch gerade, dass die im Gegensatz zu normalen enums typsicher sind. Natürlich könnte es verschiedene enum class mit gleich benannten Membern geben, aber dank Typsicherheit sollte das doch nie zu Problemen führen können. Insbesondere bei so direkten Zuweisungen oder Abfragen finde ich es doch sehr lästig.
Irgendwo geht für mich da der ganze Sinn des enums verloren, wenn ich immer den vollen Namen angeben muss. Da kann ich ja auch gleich globale Konstanten verwenden und mir die Doppelpunkte sparen. Die Idee sollte doch sein (wie bei namespaces und Klassen), das man jeweils nur so spezifisch sein muss, wie gerade nötig.
Also, mache ich hier einfach etwas falsch, oder ist C++ an dieser Stelle tatsächlich so doof? Und wenn ja, gibt es dafür einen vernünftigen Grund?

Re: enum class ohne enum Name verwenden

Verfasst: 07.10.2021, 14:42
von Schrompf
Wüsste nix, was da helfen könnte. Kann man den Enum vielleicht wie einen Namespace using'en?

Die Fallhöhe ist aber auch niedriger, wenn man vorher wegen vereinzelter Namenskonflikte die Angewohnheit entwickelt hat, jeden Wert eines Enums mit dem Enum zu beginnen:

Code: Alles auswählen

enum Bla { Bla_Blubber, Bla_Quiffel, Bla_Knoerpel }; 
Dann kann man das Vorwort weglassen und ist mit der neuen Methode quasi auf der selben Tippmenge, nur halt typsicher.

Re: enum class ohne enum Name verwenden

Verfasst: 07.10.2021, 14:56
von Krishty
using enum ZombieState;

Beim Lesen der Dokumentation habe ich auch direkt gelernt, dass man enum class seit C++17 als typsichere beliebige Integer-Werte einsetzen kann:

Code: Alles auswählen

enum class FileDescriptor : int { };

FileDescriptor openLog() {
    return FileDescriptor{ open("/var/log/foo") }; // OK
}

int log = openLog(); // error: cannot convert from 'FileDescriptor' to 'int'
das ist awesome

Re: enum class ohne enum Name verwenden

Verfasst: 07.10.2021, 15:40
von Lord Delvin
Im Prinzip ist das einfach ein Subtyp des Speichertyps hinter dem Doppelpunkt.
Ich würde bei enums immer den Speichertyp mit angeben. Ist ziemlich hässlich, wenn das fehlt und sich irgendwo irgendwer drauf verlässt.

Re: enum class ohne enum Name verwenden

Verfasst: 07.10.2021, 16:05
von Krishty
Ich bezog mich darauf, dass das ein Spezialfall der Initialisierungsregeln ist:

Code: Alles auswählen

enum byte : unsigned char {}; // byte is a new integer type
byte a = 42;         // error
byte b(42);          // error
byte c { 42 };       // OK as of C++17 (direct-list-initialization)
byte d = { 42 };     // error
byte e = byte{ 42 }; // OK as of C++17; same value as b
byte f { -1 };       // error
Extra, um solche Sachen wie Handles zu vereinfachen ohne dass man struct einsetzen muss.
Krishty hat geschrieben: 21.07.2019, 11:07 GIF zum Thema via http://mikelui.io/2019/01/03/seriously-bonkers.html

Bild

Re: enum class ohne enum Name verwenden

Verfasst: 07.10.2021, 21:43
von Schrompf
Oh cool, das wusste ich auch noch nicht. Das spart mir viele kleine struct SomeTypeSafeIndex { size_t idx; };

Re: enum class ohne enum Name verwenden

Verfasst: 08.10.2021, 15:55
von dot
Jonathan hat geschrieben: 07.10.2021, 13:55 Wieso muss ich wenn ich eine Konstante aus dem enum verwende immer explizit dessen Namen angeben? Also, die Idee hinter enum class ist doch gerade, dass die im Gegensatz zu normalen enums typsicher sind. Natürlich könnte es verschiedene enum class mit gleich benannten Membern geben, aber dank Typsicherheit sollte das doch nie zu Problemen führen können. Insbesondere bei so direkten Zuweisungen oder Abfragen finde ich es doch sehr lästig.
Irgendwo geht für mich da der ganze Sinn des enums verloren, wenn ich immer den vollen Namen angeben muss. Da kann ich ja auch gleich globale Konstanten verwenden und mir die Doppelpunkte sparen. Die Idee sollte doch sein (wie bei namespaces und Klassen), das man jeweils nur so spezifisch sein muss, wie gerade nötig.
Also, mache ich hier einfach etwas falsch, oder ist C++ an dieser Stelle tatsächlich so doof? Und wenn ja, gibt es dafür einen vernünftigen Grund?
Es gibt einen vernünftigen Grund: Namen wie IDLE in den globalen Scope zu leaken ist einfach eine schlechte Idee. enum class sind sogenannte "Scoped Enumerations". Wie der Name schon sagt, ist die Idee, dass die Namen der Enumerators eben nicht in den Parentscope leaken. Aber ja, implizite Konvertierungen gibt's bei denen auch nicht. Weil die sind auch eine schlechte Idee… 😉

Und nein, ein Satz globaler Konstanten definiert keinen eigenständigen Typ, ist also nicht wirklich vergleichbar.

Was dein Codebeispiel angeht, C++20 using enum to the rescue:

Code: Alles auswählen

enum class ZombieState { IDLE, WALK_AROUND };

ZombieState state;

switch(state)
{
using enum ZombieState;  // <-

case IDLE: // geht
	break;
case WALK_AROUND: // geht :)
	break;
}

Re: enum class ohne enum Name verwenden

Verfasst: 08.10.2021, 17:37
von Lord Delvin
Wegen solchen Diskussionen mag ich die Community so :)

Re: enum class ohne enum Name verwenden

Verfasst: 08.10.2021, 20:02
von Jonathan
dot hat geschrieben: 08.10.2021, 15:55 Es gibt einen vernünftigen Grund: Namen wie IDLE in den globalen Scope zu leaken ist einfach eine schlechte Idee. enum class sind sogenannte "Scoped Enumerations". Wie der Name schon sagt, ist die Idee, dass die Namen der Enumerators eben nicht in den Parentscope leaken. Aber ja, implizite Konvertierungen gibt's bei denen auch nicht. Weil die sind auch eine schlechte Idee… 😉
Ich habe ein wenig mehr darüber nachgedacht, und mir fiel auf, dass das was ich mir wünsche tatsächlich recht anders funktionieren würde, als es Bezeichner in C++ normalerweise tun.
Natürlich will ich "IDLE" nicht im globalen Scope haben. Ich will es kontextabhängig automatisch auflösen. In einem Ausdruck, in dem an einer bestimmten Stelle nur ein ZombieState sinnvoll (d.h. kein Typfehler) wäre, sollte IDLE dann automatisch als ZombieState interpretiert werden. Klar, wenn es irgendwo ein Untertyp von int ist, wäre ggf. ein "auto state = IDLE + 5" auch ein möglicher Ausdruck, da den Typen herzuleiten ist natürlich irgendwie problematisch. Aber wenn man sich anschaut, was man mit Scoped Enumerations nunmal in der Praxis macht, dann sind das zu 98% simple Abfrage (state == IDLE) oder Zuweisungen (state = IDLE). Wenn für diese zwei Fälle Sonderregeln existieren würden, wären die Dinger schon signifikant besser zu verwenden. Das ist philosophisch betrachtet vielleicht hässlich, weil es ein Ausnahmefall ist und komplexere Ausdrücke direkt scheitern würden, Aber da könnte C++ halt einfach auch mal ein bisschen pragmatischer und benutzbarer sein. Sonderregeln sind vielleicht nervig für das Compilerteam, aber das war ja in C++ auch noch nie ein gültiges Argument :D

Re: enum class ohne enum Name verwenden

Verfasst: 08.10.2021, 20:33
von Krishty
Jonathan hat geschrieben: 08.10.2021, 20:02Natürlich will ich "IDLE" nicht im globalen Scope haben. Ich will es kontextabhängig automatisch auflösen. In einem Ausdruck, in dem an einer bestimmten Stelle nur ein ZombieState sinnvoll (d.h. kein Typfehler) wäre, sollte IDLE dann automatisch als ZombieState interpretiert werden. […] Wenn für diese zwei Fälle Sonderregeln existieren würden, wären die Dinger schon signifikant besser zu verwenden.
AFAIK hatte Cat mein Gejammer 2013 oder so zum Anlass genommen, ein Proposal für genau das zu schreiben. Vielleicht aber auch nur für case-Labels, denn dort kennt man den Typ ja definitiv durch den Typ im switch-Ausdruck. Ist offensichtlich nichts draus geworden, aber ich weiß nicht, warum.

(Dass ich bei switch(day) jedes Mal case Weekday::mondae schreiben muss statt schlicht case mondae, ist echt der Gipfel der Perversion. Bei int oder altem enum, okay. Aber bei enum class?! Diese Idioten)

Re: enum class ohne enum Name verwenden

Verfasst: 09.10.2021, 11:59
von Lord Delvin
Man könnte bei einem Nameresolution Error als Fallback nochmal einen closed lookup im expected Type machen.
*Allerdings* hat man in einer Sprache wie C++ natürlich das Problem, dass jeder Teil des letzten Satzes überall funktionieren muss.
Außerdem musst du den expected Type an der Stelle kennen. Das wird für einfache Fälle sicher der Fall sein. Ob man das im Allgemeinen auch kann, wäre jetzt eine Frage, die man irgendwen in der ISO WG fragen müsste und bekäme vermutlich trotzdem eine "muss ich prüfen" Antwort.
Die Regel mit nur switch wird einem vermutlich kaum helfen, weil switch/case mit anderen Kontrollflusselementen überlagern kann. Das wird daraus ein nichttriviales Problem machen.
Wenn ich das richtig im Kopf hatte, durfte man da sowas machen wie

Code: Alles auswählen

switch(x) { case A: if(b) { case B: } else {...}}

Re: enum class ohne enum Name verwenden

Verfasst: 09.10.2021, 12:51
von NytroX
Also ich denke, dass der Compiler in den meisten Fällen das enum Wissen könnte.
Im switch ist es logisch, weil man ja über einen bestimmten Typ switched.
Bei Funktionsaufrufen ist es aber auch der Fall.
Und bei der Initalisierung ebenso.

Bei anderen Fällen müsste man dann halt den Prefix verwenden, aber ehrlich gesagt fallen mir da nicht so viele Fälle ein.

Code: Alles auswählen

enum class ZombieState { IDLE, WALK_AROUND };
Zombie makeZombie(std::string name, ZombieState zstate);

switch(state){
	case IDLE: break; //kann man deducen, weil typeof(switch(state)) ist ZombieState
}

ZombieState state = IDLE; //ist kein assignment, sondern eine initialisierung, das weiß der Compiler
ZombieState state2(IDLE); //das auch
ZombieState state3 { IDLE }; //das auch
state = WALK_AROUND; //hier ist es kritisch
makeZombie("Zombie1", WALK_AROUND); //kann man deducen, weil typeof(arg2) ist ZombieState
auto state = ZombieState::IDLE //hier muss man es angeben, das kann der Compiler nicht wissen

aber mit using enum ist es ja schonmal ganz ok.

Re: enum class ohne enum Name verwenden

Verfasst: 10.10.2021, 09:08
von Lord Delvin
NytroX hat geschrieben: 09.10.2021, 12:51 Also ich denke, dass der Compiler in den meisten Fällen das enum Wissen könnte.
Im switch ist es logisch, weil man ja über einen bestimmten Typ switched.
Bei Funktionsaufrufen ist es aber auch der Fall.
Und bei der Initalisierung ebenso.

Bei anderen Fällen müsste man dann halt den Prefix verwenden, aber ehrlich gesagt fallen mir da nicht so viele Fälle ein.
Also Template-Funktionen, bei denen das enum einen Template-Parameter als Typ verwendet in Verbindung mit Typinferenz, die C++ meines Wissens hat.

Außerdem ist Sprachdesign eine totale Funktion. D.h. es hilft nicht, dass der Compiler *meistens* irgendwas. Entweder immer oder nicht. Und das mit "dann halt Fehler" zu quittieren, kann in so komplexen Sprachen teils extrem merkwürdige Effekte haben.
Man sollte bei so Erweiterungen auch untersuchen, wie sich das auf die Qualität von z.B. Autocompletion in der IDE auswirkt.
Da ist gerade Java ganz groß drin, Spracherweiterungen zu bauen, die die Gesamtproduktivität einfach reduzieren :-/

Re: enum class ohne enum Name verwenden

Verfasst: 11.10.2021, 18:31
von Jonathan
Lord Delvin hat geschrieben: 10.10.2021, 09:08 Außerdem ist Sprachdesign eine totale Funktion. D.h. es hilft nicht, dass der Compiler *meistens* irgendwas. Entweder immer oder nicht. Und das mit "dann halt Fehler" zu quittieren, kann in so komplexen Sprachen teils extrem merkwürdige Effekte haben.
Man sollte bei so Erweiterungen auch untersuchen, wie sich das auf die Qualität von z.B. Autocompletion in der IDE auswirkt.
Da ist gerade Java ganz groß drin, Spracherweiterungen zu bauen, die die Gesamtproduktivität einfach reduzieren :-/
Ok, aber man könnte ja durchaus z.b. den Zuweisungs- und Vergleichsoperator erweitern. Anstatt dass auf der rechten Seite ein beliebiger Ausdruck stehen darf, könnte dann da auch alternativ ein einzelner enum-Name/Bezeichner ohne Scope stehen. Das wäre etwas hässlich, da es wirklich eine ganz explizite Ausnahme für Spezialfälle wäre, aber dafür hat man dann auch keine Probleme mit möglichen Nebenwirkungen 3 Stufen tiefer. Und es würde halt 95% der Use-Cases abdecken. Vermutlich könnte man mit etwas mehr Mühe auch noch ein paar andere nützliche Fälle abdecken (z.B. bedingte Initialisierung mit dem ? Operator), so dass fast alles was noch übrig bleiben würde, eh nicht als sauberer oder schöner Code gelten würde.

Re: enum class ohne enum Name verwenden

Verfasst: 11.10.2021, 20:15
von Lord Delvin
Ich glaube es wäre einfacher generell als Fallback in den Expectedtype zu schauen, wenn man den kennt.
Zuweisungs- und Vergleichsoperator gerade in C++ wird so schon kein Spaß sein.

Ehrlich gesagt habe ich mir kurz überlegt, ob ich es in Tyr einbauen würde, um zu schauen, wie es sich anfühlt.
Abgesehen davon, dass ich noch keine Enumerationen habe, glaube ich nicht, dass es wirklich gut funktioniert.
Wenn, dann mache ich das nur in switch als lokale Lösung und nicht generell.
Meine Syntax ist aber auch viel einfacher.