Kompilieren Sie den folgenden Code und erhalten Sie den Fehler von type illegal
.
int main()
{
// Compilation error - switch expression of type illegal
switch(std::string("raj"))
{
case"sda":
}
}
Sie können keinen String entweder verwenden switch
oder case
. Warum? Gibt es eine Lösung, die gut funktioniert, um Logik zu unterstützen, die dem Einschalten von Zeichenfolgen ähnelt?
c++
string
switch-statement
yesraaj
quelle
quelle
QMetaEnum
Antworten:
Der Grund dafür hat mit dem Typensystem zu tun. C / C ++ unterstützt Zeichenfolgen als Typ nicht wirklich. Es unterstützt zwar die Idee eines konstanten char-Arrays, versteht jedoch den Begriff eines Strings nicht vollständig.
Um den Code für eine switch-Anweisung zu generieren, muss der Compiler verstehen, was es bedeutet, dass zwei Werte gleich sind. Für Elemente wie Ints und Enums ist dies ein trivialer Bitvergleich. Aber wie soll der Compiler 2 String-Werte vergleichen? Groß- und Kleinschreibung beachten, nicht berücksichtigen, kulturbewusst usw. Ohne eine vollständige Kenntnis einer Zeichenfolge kann dies nicht genau beantwortet werden.
Darüber hinaus werden C / C ++ - Switch-Anweisungen normalerweise als Verzweigungstabellen generiert . Es ist bei weitem nicht so einfach, eine Verzweigungstabelle für einen Switch im String-Stil zu generieren.
quelle
std::string
Literale hinzugefügt wurden. Es ist meistens historisch. Aber ein Problem , das in den Sinn kommt , ist , dass mit der Art und Weiseswitch
zur Zeit arbeitet, vervielfältigencase
s muss zum Zeitpunkt der Kompilierung erkannt werden; Dies ist jedoch für Zeichenfolgen möglicherweise nicht so einfach (unter Berücksichtigung der Auswahl des Laufzeitgebietsschemas usw.). Ich nehme an, dass so etwasconstexpr
Fälle erfordern oder nicht spezifiziertes Verhalten hinzufügen müsste (niemals etwas, was wir tun wollen).std::string
Werte oder sogar einstd::string
mit einem const char-Array verglichen werden sollen (nämlich unter Verwendung von operator ==). Es gibt keinen technischen Grund, der den Compiler daran hindern würde, eine switch-Anweisung für einen Typ zu generieren, der diesen Operator bereitstellt. Es würde einige Fragen zu Dingen wie der Lebensdauer der Etiketten aufwerfen, aber alles in allem ist dies in erster Linie eine Entscheidung über das Sprachdesign, keine technische Schwierigkeit.Wie bereits erwähnt, erstellen Compiler gerne Nachschlagetabellen, die
switch
Anweisungen nach Möglichkeit auf ein Timing nahe O (1) optimieren . Kombinieren Sie dies mit der Tatsache, dass die C ++ - Sprache keinen Zeichenfolgentyp hat -std::string
ist Teil der Standardbibliothek, die nicht Teil der Sprache an sich ist.Ich werde eine Alternative anbieten, die Sie vielleicht in Betracht ziehen möchten. Ich habe sie in der Vergangenheit effektiv eingesetzt. Anstatt die Zeichenfolge selbst umzuschalten, schalten Sie das Ergebnis einer Hash-Funktion um, die die Zeichenfolge als Eingabe verwendet. Ihr Code ist fast so klar wie das Umschalten der Zeichenfolge, wenn Sie einen vorgegebenen Satz von Zeichenfolgen verwenden:
Es gibt eine Reihe offensichtlicher Optimierungen, die ziemlich genau dem folgen, was der C-Compiler mit einer switch-Anweisung machen würde ... komisch, wie das passiert.
quelle
C ++
constexpr Hash-Funktion:
quelle
operator ""
, um den Code schöner zu machen.constexpr inline unsigned int operator "" _(char const * p, size_t) { return hash(p); }
Und benutze es wiecase "Peter"_: break;
DemoC ++ 11 Update von anscheinend nicht @MarmouCorp oben aber http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4067/Switch-on-Strings-in-C.htm
Verwendet zwei Maps zum Konvertieren zwischen den Zeichenfolgen und der Klassenaufzählung (besser als einfache Aufzählung, da die Werte darin enthalten sind, und umgekehrte Suche nach netten Fehlermeldungen).
Die Verwendung von statisch im Codeguru-Code ist mit Compiler-Unterstützung für Initialisierungslisten möglich, was VS 2013 plus bedeutet. gcc 4.8.1 war damit einverstanden, nicht sicher, wie weit hinten es kompatibel sein würde.
...
quelle
Das Problem ist, dass die switch-Anweisung in C ++ aus Optimierungsgründen nur für primitive Typen funktioniert und Sie sie nur mit Konstanten für die Kompilierungszeit vergleichen können.
Vermutlich liegt der Grund für die Einschränkung darin, dass der Compiler eine Form der Optimierung anwenden kann, indem er den Code auf einen cmp-Befehl und einen Goto kompiliert, bei dem die Adresse basierend auf dem Wert des Arguments zur Laufzeit berechnet wird. Da Verzweigungen und und Schleifen mit modernen CPUs nicht gut funktionieren, kann dies eine wichtige Optimierung sein.
Um dies zu umgehen, müssen Sie leider auf if-Aussagen zurückgreifen.
quelle
std::string
und andere erste Bürger in der Sprache machen und sie in switch-Anweisungen mit einem effizienten Algorithmus unterstützen können.std::map
+ C ++ 11 Lambdas-Muster ohne Aufzählungenunordered_map
für die potenziell amortisiertenO(1)
: Was ist der beste Weg, um eine HashMap in C ++ zu verwenden?Ausgabe:
Verwendung innerhalb von Methoden mit
static
Um dieses Muster in Klassen effizient zu nutzen, initialisieren Sie die Lambda-Karte statisch, oder Sie zahlen
O(n)
jedes Mal, wenn Sie sie von Grund auf neu erstellen.Hier können wir mit der
{}
Initialisierung einerstatic
Methodenvariablen durchkommen : Statische Variablen in Klassenmethoden , aber wir könnten auch die Methoden verwenden, die unter: Statische Konstruktoren in C ++? Ich muss private statische Objekte initialisierenEs war notwendig, die Lambda-Kontexterfassung
[&]
in ein Argument umzuwandeln , oder das wäre undefiniert gewesen: const static auto lambda, das mit der Erfassung als Referenz verwendet wirdBeispiel, das dieselbe Ausgabe wie oben erzeugt:
quelle
switch
Aussage ein Unterschied besteht . Das Duplizieren von Fallwerten in einerswitch
Anweisung ist ein Fehler bei der Kompilierung. Beistd::unordered_map
stillschweigender Verwendung werden doppelte Werte akzeptiert.In C ++ und C funktionieren Switches nur für ganzzahlige Typen. Verwenden Sie stattdessen eine if else-Leiter. C ++ hätte offensichtlich eine Art swich-Anweisung für Strings implementieren können - ich denke, niemand hat es für sinnvoll gehalten, und ich stimme ihnen zu.
quelle
Warum nicht? Sie können die Switch-Implementierung mit äquivalenter Syntax und derselben Semantik verwenden. Die
C
Sprache enthält überhaupt keine Objekte und Zeichenfolgenobjekte, aber Zeichenfolgen inC
sind nullterminierte Zeichenfolgen, auf die durch einen Zeiger verwiesen wird. DieC++
Sprache hat die Möglichkeit, Überladungsfunktionen für den Objektvergleich oder die Überprüfung der Objektgleichheit zu erstellen. WieC
soC++
ist flexibel genug , um solche Schalter für Strings habenC
Sprache und für Objekte jeglicher Art , dass die Unterstützung comparaison oder Scheck Gleichheit fürC++
Sprache. Und moderneC++11
ermöglichen es, diese Switch-Implementierung ausreichend effektiv zu gestalten.Ihr Code wird folgendermaßen aussehen:
Es ist beispielsweise möglich, kompliziertere Typen
std::pairs
oder beliebige Strukturen oder Klassen zu verwenden, die Gleichheitsoperationen (oder Vergleiche für den Schnellmodus ) unterstützen.Eigenschaften
Sintax Unterschiede mit Sprachwechsel ist
Für die verwendete
C++97
Sprache lineare Suche. FürC++11
und moderner möglich,quick
Modus wuth Baumsuche zu verwenden, wo return Anweisung in CASE nicht erlaubt wird. DieC
Sprachimplementierung ist vorhanden, wennchar*
Typ- und nullterminierte Zeichenfolgenvergleiche verwendet werden.Lesen Sie mehr über diese Switch-Implementierung.
quelle
Um eine Variation mit dem einfachsten möglichen Container hinzuzufügen (keine geordnete Karte erforderlich) ... Ich würde mich nicht um eine Aufzählung kümmern - setzen Sie einfach die Containerdefinition unmittelbar vor den Schalter, damit Sie leicht erkennen können, welche Zahl steht Welcher Fall.
Dies führt eine Hash-Suche in der
unordered_map
und verwendet die zugehörigeint
, um die switch-Anweisung zu steuern. Sollte ziemlich schnell sein. Beachten Sie, dassat
anstelle von verwendet wird[]
, da ich diesen Container gemacht habeconst
. Die Verwendung[]
kann gefährlich sein. Wenn sich die Zeichenfolge nicht in der Karte befindet, erstellen Sie eine neue Zuordnung und erhalten möglicherweise undefinierte Ergebnisse oder eine kontinuierlich wachsende Karte.Beachten Sie, dass die
at()
Funktion eine Ausnahme auslöst, wenn sich die Zeichenfolge nicht in der Karte befindet. Vielleicht möchten Sie zuerst mit testencount()
.Die Version mit einem Test für eine undefinierte Zeichenfolge folgt:
quelle
Ich denke, der Grund dafür ist, dass in C-Strings keine primitiven Typen sind, wie Tomjen sagte, denken Sie in einem String als char-Array, so dass Sie Dinge wie:
quelle
In C ++ sind Strings keine erstklassigen Bürger. Die Zeichenfolgenoperationen werden über die Standardbibliothek ausgeführt. Ich denke, das ist der Grund. Außerdem verwendet C ++ die Optimierung von Verzweigungstabellen, um die switch case-Anweisungen zu optimieren. Schauen Sie sich den Link an.
http://en.wikipedia.org/wiki/Switch_statement
quelle
In C ++ können Sie nur eine switch-Anweisung für int und char verwenden
quelle
long
und verwendenlong long
, was sich nicht in verwandeln wirdint
. Dort besteht keine Gefahr der Kürzung.quelle
In vielen Fällen können Sie zusätzliche Arbeit vermeiden, indem Sie das erste Zeichen aus der Zeichenfolge ziehen und einschalten. Möglicherweise müssen Sie charat (1) verschachteln, wenn Ihre Fälle mit demselben Wert beginnen. Jeder, der Ihren Code liest, würde sich über einen Hinweis freuen, da die meisten nur if-else-if prüfen würden
quelle
Weitere funktionale Problemumgehung für das Switch-Problem:
quelle
Sie können keine Zeichenfolge in Switch-Groß- und Kleinschreibung verwenden. Es sind nur int & char zulässig. Stattdessen können Sie versuchen, die Zeichenfolge mit enum darzustellen und im switch case-Block wie zu verwenden
Verwenden Sie es in der swich case-Anweisung.
quelle
Schalter funktionieren nur mit integralen Typen (int, char, bool usw.). Warum nicht eine Karte verwenden, um eine Zeichenfolge mit einer Nummer zu koppeln, und diese Nummer dann mit dem Schalter verwenden?
quelle
Das liegt daran, dass C ++ Schalter in Sprungtabellen verwandelt. Es führt eine triviale Operation an den Eingabedaten durch und springt zur richtigen Adresse, ohne sie zu vergleichen. Da eine Zeichenfolge keine Zahl, sondern ein Array von Zahlen ist, kann C ++ keine Sprungtabelle daraus erstellen.
(Code aus Wikipedia https://en.wikipedia.org/wiki/Branch_table )
quelle
cmp
/jcc
Implementierung kann nach dem C ++ Standard genauso gültig sein.