Kompilowanie następującego kodu i wystąpił błąd type illegal
.
int main()
{
// Compilation error - switch expression of type illegal
switch(std::string("raj"))
{
case"sda":
}
}
Nie można użyć łańcucha w żadnym z switch
lub case
. Czemu? Czy jest jakieś rozwiązanie, które działa ładnie, obsługując logikę podobną do przełączania ciągów?
QMetaEnum
Odpowiedzi:
Powód, dla którego ma to związek z systemem typów. C / C ++ tak naprawdę nie obsługuje ciągów jako typu. Obsługuje ideę stałej tablicy znaków, ale tak naprawdę nie w pełni rozumie pojęcie łańcucha.
Aby wygenerować kod instrukcji switch, kompilator musi zrozumieć, co to znaczy, że dwie wartości są równe. W przypadku elementów takich jak int i wyliczenia jest to trywialne porównanie bitów. Ale w jaki sposób kompilator powinien porównać 2 wartości ciągu? Rozróżniana jest wielkość liter, nieczułość, świadomość kulturowa itp. Bez pełnej świadomości łańcucha nie można dokładnie odpowiedzieć.
Ponadto instrukcje przełączania C / C ++ są zazwyczaj generowane jako tabele gałęzi . Wygenerowanie tabeli rozgałęzień dla przełącznika stylu łańcucha nie jest tak łatwe.
źródło
std::string
dodano literały. Jest to głównie historia. Ale jednym z problemów, który przychodzi mi na myśl, jest to, że przyswitch
obecnym sposobie działania duplikatycase
s muszą zostać wykryte w czasie kompilacji; może to jednak nie być takie łatwe dla łańcuchów (biorąc pod uwagę wybór ustawień regionalnych w czasie wykonywania itd.). Podejrzewam, że taka rzecz musiałaby wymagaćconstexpr
przypadków lub dodać nieokreślone zachowanie (nigdy nie jest to rzecz, którą chcemy robić).std::string
wartości, a nawetstd::string
z tablicą const char (mianowicie za pomocą operatora ==), nie ma technicznego powodu, który uniemożliwiłby kompilatorowi wygenerowanie instrukcji przełączania dla dowolnego typu udostępniającego tego operatora. Otworzyłoby to kilka pytań na temat życia lables, ale w sumie jest to przede wszystkim decyzja dotycząca projektu języka, a nie trudność techniczna.Jak wspomniano wcześniej, kompilatory lubią budować tabele wyszukiwania, które optymalizują
switch
instrukcje tak, aby zbliżały się do czasu O (1), gdy tylko jest to możliwe. Połącz to z faktem, że język C ++ nie ma typu łańcucha -std::string
jest częścią biblioteki standardowej, która nie jest częścią języka jako takiego.Oferuję alternatywę, którą możesz rozważyć, korzystałem z niej w przeszłości z dobrym skutkiem. Zamiast przełączania samego łańcucha, przełącz wynik funkcji skrótu, która używa łańcucha jako danych wejściowych. Twój kod będzie prawie tak wyraźny, jak przełączanie ciągu, jeśli używasz określonego zestawu ciągów:
Istnieje kilka oczywistych optymalizacji, które podążają za tym, co zrobiłby kompilator C z instrukcją switch ... zabawne, jak to się dzieje.
źródło
C ++
funkcja skrótu constexpr:
źródło
operator ""
aby kod był piękniejszy.constexpr inline unsigned int operator "" _(char const * p, size_t) { return hash(p); }
I używaj go jakcase "Peter"_: break;
DemoAktualizacja C ++ 11 najwyraźniej nie @MarmouCorp powyżej, ale http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4067/Switch-on-Strings-in-C.htm
Używa dwóch map do konwersji między ciągami znaków i wyliczeniem klasy (lepiej niż zwykły wyliczenie, ponieważ jego wartości są w nim zakreślone, i odwrotne wyszukiwanie w celu uzyskania miłych komunikatów o błędach).
Zastosowanie kodu statycznego w kodzie codeguru jest możliwe dzięki obsłudze kompilatora dla list inicjalizacyjnych, co oznacza VS 2013 plus. gcc 4.8.1 było w porządku, nie jestem pewien, o ile dalej będzie kompatybilny.
...
źródło
Problem polega na tym, że ze względu na optymalizację instrukcja switch w C ++ nie działa tylko na typach pierwotnych i można je porównywać tylko ze stałymi czasowymi kompilacji.
Przypuszczalnie powodem tego ograniczenia jest to, że kompilator jest w stanie zastosować jakąś formę optymalizacji kompilując kod do jednej instrukcji cmp i goto, gdzie adres jest obliczany na podstawie wartości argumentu w czasie wykonywania. Ponieważ rozgałęzienia i pętle nie działają dobrze z nowoczesnymi procesorami, może to być ważna optymalizacja.
Aby obejść ten problem, obawiam się, że będziesz musiał uciekać się do oświadczeń.
źródło
std::string
a inni pierwsi obywatele w języku i wspierać ich w instrukcji switch za pomocą wydajnego algorytmu.std::map
+ C ++ 11 wzór lambda bez wyliczeńunordered_map
dla potencjalnie zamortyzowanegoO(1)
: Jaki jest najlepszy sposób użycia HashMap w C ++?Wynik:
Użycie w metodach z
static
Aby efektywnie wykorzystać ten wzorzec wewnątrz klas, zainicjuj mapę lambda statycznie, albo płacisz za
O(n)
każdym razem, aby zbudować ją od zera.Tutaj możemy uciec od
{}
inicjalizacjistatic
zmiennej metody: Zmienne statyczne w metodach klasowych , ale moglibyśmy również użyć metod opisanych w: statyczne konstruktory w C ++? Muszę zainicjować prywatne obiekty statyczneKonieczne było przekształcenie przechwytywania kontekstu lambda
[&]
w argument, który byłby niezdefiniowany: const static auto lambda używany z przechwytywaniem przez odniesieniePrzykład, który daje takie same wyniki jak powyżej:
źródło
switch
stwierdzeniem. Powielanie wartości wielkości liter wswitch
instrukcji jest błędem czasowym kompilacji. Używanie w trybiestd::unordered_map
cichym przyjmuje zduplikowane wartości.W C ++ i C przełączniki działają tylko na typach całkowitych. Zamiast tego użyj drabiny if else. C ++ mógł oczywiście zaimplementować jakąś instrukcję swich dla łańcuchów - chyba nikt nie uważał, że warto, i zgadzam się z nimi.
źródło
Dlaczego nie? Można użyć implementacji przełącznika z równoważną składnią i taką samą semantyką.
C
Język nie posiada obiektów i smyczki obiekty w ogóle, ale w strunyC
jest null zakończone ciągi odwołują wskaźnika.C++
Język mają możliwość dokonania funkcje przeciążenie dla obiektów porównania lub sprawdzanie obiektów równości. TakC
jakC++
jest na tyle elastyczny, aby mieć taki przełącznik do ciągów dlaC
języka i dla każdego typu obiektów, które comparaison wsparcie lub równość czek naC++
języku. NowoczesneC++11
pozwalają na wystarczająco efektywne wdrożenie tego przełącznika.Twój kod będzie taki:
Możliwe jest użycie bardziej skomplikowanych typów, na przykład,
std::pairs
dowolnych struktur lub klas, które obsługują operacje równości (lub komendy dla szybkiego trybu ).cechy
Różnice Sintax z przełączaniem języków są
Do
C++97
wyszukiwania liniowego używanego języka. DoC++11
bardziej nowoczesnego możliwego do użyciaquick
trybu wyszukiwania drzewa wuth, w którym instrukcja return w CASE staje się niedozwolona.C
Realizacja język gdzie istniejechar*
używany jest typ i zerowej zakończony Porównania smyczkowych.Przeczytaj więcej o tej implementacji przełącznika.
źródło
Aby dodać odmianę przy użyciu najprostszego możliwego pojemnika (nie ma potrzeby zamówienia uporządkowanej mapy) ... nie zawracałbym sobie głowy wyliczeniem - wystarczy umieścić definicję kontenera bezpośrednio przed przełącznikiem, aby łatwo było zobaczyć, która liczba reprezentuje która sprawa
Spowoduje to wyszukiwanie skrótowe
unordered_map
i użycie powiązanegoint
do sterowania instrukcją switch. Powinno być dość szybkie. Zauważ, żeat
jest używany zamiast[]
, ponieważ zrobiłem ten pojemnikconst
. Używanie[]
może być niebezpieczne - jeśli łańcucha nie ma na mapie, utworzysz nowe mapowanie i może to skutkować nieokreślonymi wynikami lub stale rosnącą mapą.Zauważ, że
at()
funkcja zgłosi wyjątek, jeśli ciągu nie ma na mapie. Więc możesz najpierw przetestować za pomocącount()
.Wersja z testem na niezdefiniowany ciąg znaków wygląda następująco:
źródło
Myślę, że powodem jest to, że w C łańcuchy nie są prymitywnymi typami, jak powiedział tomjen, myśl w łańcuchu jako tablica char, więc nie możesz robić takich rzeczy:
źródło
W c ++ łańcuchy nie są obywatelami pierwszej klasy. Operacje na łańcuchach są wykonywane za pomocą standardowej biblioteki. Myślę, że to jest powód. Ponadto C ++ korzysta z optymalizacji tabeli rozgałęzień w celu optymalizacji instrukcji case switch. Spójrz na link.
http://en.wikipedia.org/wiki/Switch_statement
źródło
W C ++ można używać tylko instrukcji switch na int i char
źródło
long
ilong long
, co się nie zmieniint
. Nie ma tam ryzyka obcięcia.źródło
w wielu przypadkach można uniknąć dodatkowej pracy, wyciągając pierwszy znak z łańcucha i włączając go. może skończyć się koniecznością wykonania zagnieżdżonego przełącznika na charat (1), jeśli twoje przypadki zaczynają się od tej samej wartości. każdy czytający Twój kod z pewnością doceniłby podpowiedź, ponieważ większość z nich spróbowałaby tylko jeśli-inaczej-jeśli
źródło
Bardziej funkcjonalne obejście problemu z przełącznikiem:
źródło
Nie można używać ciągu znaków w przypadku przełącznika. Dozwolone są tylko int i char. Zamiast tego możesz wypróbować wyliczenie do reprezentowania ciągu i użyć go w bloku skrzynki przełączników, takim jak
Użyj go w instrukcji case swich.
źródło
Przełączniki działają tylko z typami integralnymi (int, char, bool itp.). Dlaczego nie użyć mapy do sparowania ciągu z liczbą, a następnie użyć tego numeru z przełącznikiem?
źródło
To dlatego, że C ++ zamienia przełączniki w tabele skoków. Wykonuje trywialną operację na danych wejściowych i przeskakuje pod właściwy adres bez porównywania. Ponieważ ciąg nie jest liczbą, ale tablicą liczb, C ++ nie może z niej utworzyć tabeli skoków.
(kod z wikipedii https://en.wikipedia.org/wiki/Branch_table )
źródło
cmp
/jcc
implementacja może być równie ważna zgodnie ze standardem C ++.