2016-12-20 8 views
3

Da wir Zeichenfolgen nicht direkt in Switch-Case-Anweisungen verwenden können, da sie keine Konstanten auswerten können, habe ich meine Einträge in map<string,int> gemappt.Verwenden von Map <string,int> Verwenden von Zeichenfolgen in Switch-Case-Anweisungen

map<string,int> hash; 
    hash["+x"] = 0; 
    hash["-x"] = 1; 
    hash["+y"] = 2; 
    hash["-y"] = 3; 
    hash["+z"] = 4; 
    hash["-z"] = 5; 

Nun, ich bin mit ihnen in der switch-case-Ausdruck:

cin >> bend //assume user entered +x 
switch(hash[bend]) 
      { 
       case hash["+y"] : 
        switch(pointedto) 
        { 
         case hash["+x"]: pointedto = hash["+y"]; 
          break; 
         case hash["-x"]: pointedto = hash["-y"]; 
          break; 
         case hash["+y"]: pointedto = hash["-x"]; 
          break; 
         case hash["-y"]: pointedto = hash["+x"]; 
          break; 
         case hash["+z"]: pointedto = hash["+z"]; 
          break; 
         case hash["-z"]: pointedto = hash["-z"]; 
          break;     
        } 
      } 

Ich bin immer Fehler: an array reference cannot appear in a constant-expression für die alle case s. Ich erwartete hash["+x"] und andere, int zurückzukehren, was zu einer Konstante führen wird.

PS: die andere Alternative ist constexpr von C++11, aber ich war neugierig, dieses zu verwenden.

+2

Ein konstanter Ausdruck ist etwas, das zur Kompilierzeit ausgewertet werden kann. 'std :: map :: operator []' ist keines dieser Dinge. – DeiDei

+0

_Ich erwartete Hash ["+ x"] und andere, um int * zurückzugeben, was zu einer Konstante führt. * _ Was Sie erwarten, hat ["etwas"] wird zu einer Konstanten führen = –

+0

Es ist * möglich Erstellen Sie eine Map, die durch Strings indiziert werden kann, die einen konstanten Ausdruck ergeben. Die Erstellung erfordert ein wenig Aufwand. Meine [CppCon 2016] (https://cppcon.org/2016program/) zeigte, wie man eine Basisversion erstellt. Das [Video] (https://channel9.msdn.com/Events/CPP/CppCon-2016/CppCon-2016-Dietmar-Khl-Constant-Fun) ist aus meiner Entwickler-Story verlinkt. –

Antwort

3

Kämpfen Sie nicht die Sprache.

Der Fall Etikett in einer C++ switch Anweisung müssen, dass Kompilierung auswertbare konstante Ausdrücke und eine C++ Standardbibliothek std::map oder std::unordered_map derzeit nicht zur Verfügung stellen zu können. Verwenden Sie stattdessen einen ifelse Block. Es kann sogar schneller gehen - vor allem, wenn Sie eine optimale Reihenfolge wählen.

switch ing auf einem Zeichenarray (z.B. '+x', die Apostroph-Zeichen beachten) wird oft als eine Alternative für mooted 4 Zeichen oder weniger, aber auch das ist nicht tragbar.

+1

Meh, du hast Recht. Wenn Sie mich fragen, wäre der einzige Platz, um Schalter zu benutzen, über Enumerationswerte. – DeiDei

4

Das Problem in diesem bestimmten Codebeispiel ist, dass der Wert case eine Kompilierzeitkonstante sein sollte. Der von std::map::operator[] zurückgegebene Wert ist nicht, daher kann er nicht in der Klausel case verwendet werden.

könnten Sie verwenden den constexpr -basierte Ansatz, wie folgt aus:

constexpr array<double, 10> values = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0}; 

constexpr int f(double f) { 
    for (int i = 0; i < 10; ++i) { 
     if (values[i] == f) { 
      return i; 
     } 
    } 
    return -1; 
} 

double x = 4.0; 
switch (f(x)) { 
case f(1.0) : 
    break; 
case f(2.0) : 
    break; 
    ... 
default : 
    break; 
} 

Aber es funktioniert nur für constexpr -constructible Typen. Leider ist std::string nicht einer von ihnen.

+0

Woah ... warum habe ich das ignoriert? Auf der einen Seite spreche ich "conexpr" und auf der anderen Seite erwarte ich, dass "switch-case" prädiktiv ist. Ich denke ich sollte jetzt ins Bett gehen: O –

+2

Java erlaubt das Einschalten von Strings übrigens. Aber es wäre eine Schande, noch einen anderen C++ - Typ an die dunkle Seite zu verlieren. – Bathsheba

+1

@Bathsheba: Danke, dass du mich gerettet hast;) –

1

Einige Lösungen:

  1. Lassen Sie sich so früh wie möglich von den Saiten befreien. Deklarieren Sie enum direction {posX, negX, posY, negY, posZ, negZ} und deklarieren Sie pointedto, um vom Typ direction statt einer Zeichenfolge zu sein. Jetzt können Sie sagen case posY:, die Kompilierzeit konstant ist, aber besser lesbar als case 2:. Ändern Sie hash in , um Eingaben vom Benutzer zu analysieren, führen Sie Ihre gesamte Verarbeitung mit aus, und definieren Sie map<direction, string>, um zurück in eine Zeichenfolge zu übersetzen, wenn Sie dem Benutzer eine Antwort präsentieren möchten.

  2. Erkennen, dass das Suchen einer Karte "ein bisschen wie" ein Schalter ist.Ein Switch, der einen verschachtelten Switch enthält, könnte durch eine Karte von Maps (map<direction, map<direction, direction>>) emuliert werden, so dass alle Switches durch eine Zeile ersetzt werden könnten: pointedto = hash[bend][pointedto]; ODER die verschachtelten Switches könnten durch eine Karte mit einem "Compound Key" ersetzt werden (map<pair<direction, direction>, direction>), so dass alle Schalter durch pointedto = hash[make_pair(bend, pointedto)]; ersetzt werden. Dies funktioniert mit bend und pointedto als Strings - also hash["+y"]["+x"] == "+y" - aber wenn Sie eine Reihe von Iterationen zwischen Benutzereingabe und -ausgabe durchführen, können Enums effizienter sein.

  3. Deklarieren Sie eine Enumeration mit geschickt ausgewählten Werten, z. B. enum direction {posX=4, negX=0, posY=5, negY=1, posZ=6, negZ=2}, und finden Sie einige clevere mathematische Formeln, um bend und pointiert zu einem neuen pointierten Wert zu transformieren. HINWEIS: Die Werte, die ich hier illustriert habe, wurden nur zur Veranschaulichung erfunden - und diese Route sollte nur verwendet werden, wenn Sie wirklich diesen Code für die Leistung optimieren müssen. Ich erwähne diesen Weg der Vollständigkeit halber, sondern übernehmen keine Verantwortung für schlaflose Nächte, wenn Sie sich entscheiden, mit ihm zu gehen :)

EDIT

  1. This:
#include <algorithm> 
#include <iostream> 
#include <map> 
#include <string> 

enum direction {posX, negX, posY, negY, posZ, negZ}; 

const direction transform[6][6]{ 
    // previous pointed to:     bend 
    // +x -x +y -y +z -z  ---- 
    {         }, // +x 
    {         }, // -x 
    { posY, negY, negX, posX, posZ, negZ}, // +y 
    {         }, // -y 
    {         }, // +z 
    {         }, // -z 
}; 

const std::map<std::string, direction> stringToDirection{ { "+x", posX },{ "-x", negX },{ "+y", posY },{ "-y", negY },{ "+z", posZ },{ "-z", negZ } }; 
const std::map<direction, std::string> directionToString{ { posX, "+x" },{ negX, "-x" },{ posY, "+y" },{ negY, "-y" },{ posZ, "+z" },{ negZ, "-z" } }; 

int main(int, char**) { 
    direction pointedto = posY; 

    std::string bendInput; 
    std::cin >> bendInput; 
    direction bend = stringToDirection.at(bendInput); 

    pointedto = transform[bend][pointedto]; 

    std::cout << directionToString.at(pointedto) << "\n"; 

    return 0; 
} 

Hinweise:

  • Sie müssen den Rest der Array-Initialisierung ausfüllen.
  • In diesem Beispiel habe ich keine Fehlerprüfung bei der Benutzereingabe.
  • directionToString sollte von stringToDirection generiert werden, aber ich wollte die Dinge hier einfach halten.
Verwandte Themen