switch 문을 문자열에 적용할 수 없는 이유는 무엇입니까?
를 컴파일하면 가 나타납니다.type illegal
.
int main()
{
// Compilation error - switch expression of type illegal
switch(std::string("raj"))
{
case"sda":
}
}
쪽에도 할 수 .switch
★★★★★★★★★★★★★★★★★」case
하는 데 이 되는 스트링을 켜는 것과 유사한 논리를 지원하는 데 도움이 되는 솔루션이 있습니까?
가 유형 시스템과 관련이 있는 이유.C/C++ 는, 문자열의 타입을 서포트하고 있지 않습니다.고정 문자 배열의 개념을 지원하지만 문자열의 개념을 완전히 이해하지 못합니다.
스위치 스테이트먼트의 코드를 생성하려면 컴파일러는 2개의 값이 같다는 의미를 이해해야 합니다.ints 및 enums 등의 항목에서는 이것은 간단한 비트 비교입니다.하지만 컴파일러는 두 문자열 값을 어떻게 비교해야 할까요?대소문자를 구분하지 않음, 문화 인식 등...문자열이 완전히 인식되지 않으면 정확하게 답변할 수 없습니다.
또한 C/C++ switch 문은 보통 브랜치테이블로 생성됩니다.스트링 스타일 스위치의 분기 테이블을 생성하는 것은 쉽지 않습니다.
한 바와 같이 는 '최적화' .switch
O(1)는 O(1)는 C타입이 과 C++언어를 .std::string
는 언어 자체의 일부가 아닌 표준 라이브러리의 일부입니다.
당신이 고려해 볼 만한 대안을 제시하겠습니다, 저는 예전에 잘 사용했으니까요.문자열 자체를 전환하는 대신 문자열을 입력으로 사용하는 해시 함수의 결과를 전환합니다.미리 정해진 문자열 세트를 사용하는 경우 코드는 문자열 전환과 거의 같은 수준으로 명확해집니다.
enum string_code {
eFred,
eBarney,
eWilma,
eBetty,
...
};
string_code hashit (std::string const& inString) {
if (inString == "Fred") return eFred;
if (inString == "Barney") return eBarney;
...
}
void foo() {
switch (hashit(stringValue)) {
case eFred:
...
case eBarney:
...
}
}
C 컴파일러가 스위치 스테이트먼트에서 수행하는 작업을 거의 따라가는 명백한 최적화가 많이 있습니다.어떻게 그런 일이 일어나는지 웃기네요
C++
constexpr 해시 함수:
constexpr unsigned int hash(const char *s, int off = 0) {
return !s[off] ? 5381 : (hash(s, off+1)*33) ^ s[off];
}
switch( hash(str) ){
case hash("one") : // do something
case hash("two") : // do something
}
업데이트:
C++11로 하다 있습니다.constexpr
을 사용하다C++로 하다
C++14 및 C++17에서는 다음 해시 함수를 사용할 수 있습니다.
constexpr uint32_t hash(const char* data, size_t const size) noexcept{
uint32_t hash = 5381;
for(const char *c = data; c < data + size; ++c)
hash = ((hash << 5) + hash) + (unsigned char) *c;
return hash;
}
또, C++에는 「C++17」이 .std::string_view
해서 '아까보다' 'const char *
.
에서는 C++20 을 .consteval
.
위의 @MarmouCorp이 아닌 http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4067/Switch-on-Strings-in-C.htm의 C++11 업데이트
2개의 맵을 사용하여 문자열과 클래스 열거형 간에 변환합니다(이 값은 안에 스코프가 적용되어 있기 때문에 플레인 열거형보다 우수하며 nice 오류 메시지에 대해서는 역방향 룩업이 좋습니다).
코드구루 코드의 스태틱 사용은 VS 2013 plus를 의미하는 이니셜라이저 목록에 대한 컴파일러 지원을 통해 가능합니다.gcc 4.8.1은 문제가 없었으며, 얼마나 이전 버전에서도 호환성이 있는지 알 수 없습니다.
/// <summary>
/// Enum for String values we want to switch on
/// </summary>
enum class TestType
{
SetType,
GetType
};
/// <summary>
/// Map from strings to enum values
/// </summary>
std::map<std::string, TestType> MnCTest::s_mapStringToTestType =
{
{ "setType", TestType::SetType },
{ "getType", TestType::GetType }
};
/// <summary>
/// Map from enum values to strings
/// </summary>
std::map<TestType, std::string> MnCTest::s_mapTestTypeToString
{
{TestType::SetType, "setType"},
{TestType::GetType, "getType"},
};
...
std::string someString = "setType";
TestType testType = s_mapStringToTestType[someString];
switch (testType)
{
case TestType::SetType:
break;
case TestType::GetType:
break;
default:
LogError("Unknown TestType ", s_mapTestTypeToString[testType]);
}
문제는 최적화를 위해 C++의 스위치 문은 원시 유형 이외에는 동작하지 않으며 컴파일 시간 상수와만 비교할 수 있다는 것입니다.
아마도 제한의 이유는 컴파일러가 코드를 컴파일하는 어떤 형태의 최적화를 하나의 cmp 명령과 실행 시 인수 값에 따라 주소가 계산되는 goto에 적용할 수 있기 때문일 것입니다.최신 CPU에서는 분기 및 루프가 제대로 작동하지 않기 때문에 이는 중요한 최적화가 될 수 있습니다.
이 문제를 해결하려면 if 진술에 의존해야 할 것 같습니다.
std::map
+ ( 없음)+ C++11 람다 패턴 (C++11
unordered_map
에 O(1)
: C++에서 HashMap을 사용하는 가장 좋은 방법은 무엇입니까?
#include <functional>
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
int main() {
int result;
const std::unordered_map<std::string,std::function<void()>> m{
{"one", [&](){ result = 1; }},
{"two", [&](){ result = 2; }},
{"three", [&](){ result = 3; }},
};
const auto end = m.end();
std::vector<std::string> strings{"one", "two", "three", "foobar"};
for (const auto& s : strings) {
auto it = m.find(s);
if (it != end) {
it->second();
} else {
result = -1;
}
std::cout << s << " " << result << std::endl;
}
}
출력:
one 1
two 2
three 3
foobar -1
「」를 사용한 의 사용 .static
클래스에서 이 않으면 '람다 맵'을합니다. 그렇지 않으면 비용을 지불해야 합니다.O(n)
이치노릇을 하다
.{}
의 static
메서드 변수:클래스 메서드의 스태틱 변수입니다만, C++의 스태틱컨스트럭터에서도 사용할 수 있습니다. 개인 정적 개체를 초기화해야 합니다.
캡처를 .[&]
constatic auto lambda는 capture by reference와 함께 사용됩니다.
위와 같은 출력을 생성하는 예:
#include <functional>
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
class RangeSwitch {
public:
void method(std::string key, int &result) {
static const std::unordered_map<std::string,std::function<void(int&)>> m{
{"one", [](int& result){ result = 1; }},
{"two", [](int& result){ result = 2; }},
{"three", [](int& result){ result = 3; }},
};
static const auto end = m.end();
auto it = m.find(key);
if (it != end) {
it->second(result);
} else {
result = -1;
}
}
};
int main() {
RangeSwitch rangeSwitch;
int result;
std::vector<std::string> strings{"one", "two", "three", "foobar"};
for (const auto& s : strings) {
rangeSwitch.method(s, result);
std::cout << s << " " << result << std::endl;
}
}
가능한 한 간단한 컨테이너를 사용하여 변형을 추가하려면(순서 맵이 필요 없음)...열거할 필요는 없습니다.컨테이너 정의를 스위치 바로 앞에 두면 어떤 숫자가 어떤 경우를 나타내는지 쉽게 알 수 있습니다.
됩니다.unordered_map
거기에 을 사용합니다.int
스테이트먼트switch 를 실행합니다.다음 점에 주의해 주십시오.at
.[]
그 를 만들었기const
.★★★★★★ 。[]
위험할 수 있습니다.이 문자열이 맵에 없으면 새로운 매핑이 생성되어 정의되지 않은 결과나 계속 증가하는 맵이 발생할 수 있습니다.
에 주의:at()
문자열이 맵에 없으면 함수가 예외를 발생시킵니다. 먼저 것 요.count()
.
const static std::unordered_map<std::string,int> string_to_case{
{"raj",1},
{"ben",2}
};
switch(string_to_case.at("raj")) {
case 1: // this is the "raj" case
break;
case 2: // this is the "ben" case
break;
}
정의되지 않은 문자열에 대한 테스트가 있는 버전은 다음과 같습니다.
const static std::unordered_map<std::string,int> string_to_case{
{"raj",1},
{"ben",2}
};
// in C++20, you can replace .count with .contains
switch(string_to_case.count("raj") ? string_to_case.at("raj") : 0) {
case 1: // this is the "raj" case
break;
case 2: // this is the "ben" case
break;
case 0: //this is for the undefined case
}
C++ 및 C 스위치는 정수형에서만 작동합니다.대신 if else 사다리를 사용하세요.C++는 분명히 문자열에 대해 일종의 스위치 스테이트먼트를 구현할 수 있었을 것입니다.아무도 그럴 가치가 있다고 생각하지 않았을 것이고, 저도 그 말에 동의합니다.
왜 안 되나요?스위치 실장은 동등한 구문과 동일한 시멘틱으로 사용할 수 있습니다.그C
전혀 은 오브젝트나 문자열이 .C
포인터에 의해 참조되는 늘 종단 문자열입니다.C++
언어는 오브젝트 비교나 오브젝트 동등성 확인을 위해 과부하 함수를 만들 수 있습니다.~로C
as ~하듯이C++
트 링 에 응 하 위 질 도 을 연 유 로 is정다있니 for for수합 strings to switch flexible스 enough가대스를치는 such have isC
어 ison 언 및 포 컴 넌 원 등 인 오 모 든 형 의 유 language트 브 젝 확 comp는동하 for oraraC++
언어.언어. And modern 그리고 모던C++11
이 스위치 구현이 충분히 효과적일 수 있습니다.이 스위치를 충분히 유효하게 실장할 수 있도록 합니다.
코드는 다음과 같습니다.
std::string name = "Alice";
std::string gender = "boy";
std::string role;
SWITCH(name)
CASE("Alice") FALL
CASE("Carol") gender = "girl"; FALL
CASE("Bob") FALL
CASE("Dave") role = "participant"; BREAK
CASE("Mallory") FALL
CASE("Trudy") role = "attacker"; BREAK
CASE("Peggy") gender = "girl"; FALL
CASE("Victor") role = "verifier"; BREAK
DEFAULT role = "other";
END
// the role will be: "participant"
// the gender will be: "girl"
를 어 들 잡 형 용 습 을 니 할있 it수 is example다사예 complic유 forated to more복더 possible use한 types it is예 example..std::pairs
또는 동일 연산을 지원하는 구조 또는 클래스(또는 빠른 모드의 경우 충돌)를 모두 지원합니다.
특징들
- 비교 또는 동일성을 확인하는 데 도움이 되는 모든 것
- 캐스케이드 중첩 스위치 스테이트멘을 구축할 가능성.
- 케이스 스테이트먼트를 깨거나 실패할 가능성
- 불변 대소문자 표현 사용 가능성
- 트리 검색을 사용하여 빠른 정적/동적 모드를 활성화할 수 있습니다(C++11의 경우).
언어 스위치와의 신탁스 차이는
- 대문자 키워드
- CASE 문장에 괄호 필요
- 문의 끝에 세미콜론 ';'이(가) 허용되지 않습니다.
- CASE 문의 콜론 ':'은 허용되지 않습니다.
- CASE 문의 끝에 BREAK 키워드 또는 FALL 키워드 중 하나가 필요합니다.
★★★의 C++97
언어를 사용하여 선형 검색을 수행합니다.★★★의 C++11
보다 이 있습니다.quick
모드 wuth 트리 검색에서는 CASE의 반환문이 허용되지 않습니다.그C
은 '어느 곳에 있는가'에 합니다.char*
타입과 제로 종단 문자열의 비교가 사용됩니다.
이 스위치의 실장에 대한 자세한 내용은 여기를 참조해 주세요.
C 문자열은 tomjen이 말한 것처럼 원시형이 아니기 때문에 문자열을 char 배열로 생각하기 때문에 다음과 같은 작업을 할 수 없다고 생각합니다.
switch (char[]) { // ...
switch (int[]) { // ...
c++ 문자열은 퍼스트 클래스 시민이 아닙니다.문자열 작업은 표준 라이브러리를 통해 수행됩니다.그래서 그런 것 같아요.또한 C++는 브랜치테이블 최적화를 사용하여 스위치의 케이스문을 최적화합니다.링크를 보세요.
http://en.wikipedia.org/wiki/Switch_statement
파티에 늦게, 제가 얼마 전에 생각해 낸 해결책이 있습니다. 이것은 요청된 구문에 완전히 부합합니다.
#include <uberswitch/uberswitch.hpp>
int main()
{
uswitch (std::string("raj"))
{
ucase ("sda"): /* ... */ break; //notice the parenthesis around the value.
}
}
다음은 코드입니다.https://github.com/falemagn/uberswitch
하여 ""를 할 수 .constexpr
컴파일 시 인덱스로 변환합니다.
constexpr const char* arr[] = { "bar", "foo" };
constexpr int index(const char* str) { /*...*/ }
do_something(std::string str)
{
switch(quick_index(str))
{
case index("bar"):
// ...
break;
case index("foo"):
// ...
break;
case -1:
default:
// ...
break;
}
★★★의 quick_index
없다constexpr
예를 들면, '어울리다', '어울리다'를 사용할 수 있습니다unordered_map
실행 시 O(1)를 수행합니다(또는 어레이를 정렬하고 바이너리 검색을 사용하는 경우, 예를 보려면 여기를 참조하십시오.
이 있습니다.constexpr
comparrstring comparr.되지 않음([/FONT CHANGE:/FONT CHANGE:/FONT CHANGE:/FONT CHANGE:])index
-1
는 컴파일 시에 검출됩니다.실종사례는 분명히 발견되지 않는다.C++ 에서는, 「C++」의 유연성이 .constexpr
을 사용법
#include <iostream>
#include <algorithm>
#include <unordered_map>
constexpr const char* arr[] = { "bar", "foo", "foobar" };
constexpr int cmp(const char* str1, const char* str2)
{
return *str1 == *str2 && (!*str1 || cmp(str1+1, str2+1));
}
constexpr int index(const char* str, int pos=0)
{
return pos == sizeof(arr)/sizeof(arr[0]) ? -1 : cmp(str, arr[pos]) ? pos : index(str,pos+1);
}
int main()
{
// initialize hash table once
std::unordered_map<std::string,int> lookup;
int i = 0;
for(auto s : arr) lookup[s] = i++;
auto quick_index = [&](std::string& s)
{ auto it = lookup.find(s); return it == lookup.end() ? -1 : it->second; };
// usage in code
std::string str = "bar";
switch(quick_index(str))
{
case index("bar"):
std::cout << "bartender" << std::endl;
break;
case index("foo"):
std::cout << "fighter" << std::endl;
break;
case index("foobar"):
std::cout << "fighter bartender" << std::endl;
break;
case -1:
default:
std::cout << "moo" << std::endl;
break;
}
}
닉의 해결책에 대한 토끼의 코멘트는 정말 멋있다.완전한 코드 예(C++11):
constexpr uint32_t hash(const std::string& s) noexcept
{
uint32_t hash = 5381;
for (const auto& c : s)
hash = ((hash << 5) + hash) + (unsigned char)c;
return hash;
}
constexpr inline uint32_t operator"" _(char const* p, size_t) { return hash(p); }
std::string s = "raj";
switch (hash(s)) {
case "sda"_:
// do_something();
break;
default:
break;
}
C++에서는 int와 char에서만 switch 문을 사용할 수 있습니다.
cout << "\nEnter word to select your choice\n";
cout << "ex to exit program (0)\n";
cout << "m to set month(1)\n";
cout << "y to set year(2)\n";
cout << "rm to return the month(4)\n";
cout << "ry to return year(5)\n";
cout << "pc to print the calendar for a month(6)\n";
cout << "fdc to print the first day of the month(1)\n";
cin >> c;
cout << endl;
a = c.compare("ex") ?c.compare("m") ?c.compare("y") ? c.compare("rm")?c.compare("ry") ? c.compare("pc") ? c.compare("fdc") ? 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0;
switch (a)
{
case 0:
return 1;
case 1: ///m
{
cout << "enter month\n";
cin >> c;
cout << endl;
myCalendar.setMonth(c);
break;
}
case 2:
cout << "Enter year(yyyy)\n";
cin >> y;
cout << endl;
myCalendar.setYear(y);
break;
case 3:
myCalendar.getMonth();
break;
case 4:
myCalendar.getYear();
case 5:
cout << "Enter month and year\n";
cin >> c >> y;
cout << endl;
myCalendar.almanaq(c,y);
break;
case 6:
break;
}
스위치 문제의 보다 기능적인 회피책:
class APIHandlerImpl
{
// define map of "cases"
std::map<string, std::function<void(server*, websocketpp::connection_hdl, string)>> in_events;
public:
APIHandlerImpl()
{
// bind handler method in constructor
in_events["/hello"] = std::bind(&APIHandlerImpl::handleHello, this, _1, _2, _3);
in_events["/bye"] = std::bind(&APIHandlerImpl::handleBye, this, _1, _2, _3);
}
void onEvent(string event = "/hello", string data = "{}")
{
// execute event based on incomming event
in_events[event](s, hdl, data);
}
void APIHandlerImpl::handleHello(server* s, websocketpp::connection_hdl hdl, string data)
{
// ...
}
void APIHandlerImpl::handleBye(server* s, websocketpp::connection_hdl hdl, string data)
{
// ...
}
}
문자열의 스위치를 사용할 수 있습니다.필요한 것은 문자열 표입니다. 모든 문자열을 확인합니다.
char** strings[4] = {"Banana", "Watermelon", "Apple", "Orange"};
unsigned get_case_string(char* str, char** _strings, unsigned n)
{
while(n)
{
n--
if(strcmp(str, _strings[n]) == 0) return n;
}
return 0;
}
unsigned index = get_case_string("Banana", strings, 4);
switch(index)
{
case 1: break;/*Found string `Banana`*/
default: /*No string*/
}
스위치 케이스에서는 문자열을 사용할 수 없습니다.int 및 char만 허용됩니다.대신 Enum을 사용하여 문자열을 표현하고 스위치케이스 블록에서 다음과 같이 사용할 수 있습니다.
enum MyString(raj,taj,aaj);
스위치 케이스 스테이트먼트에 사용합니다.
그것은 C++가 스위치를 점프 테이블로 바꾸기 때문입니다.입력 데이터에 대해 간단한 연산을 수행하고 비교하지 않고 적절한 주소로 점프합니다.문자열은 숫자가 아니라 숫자의 배열이므로 C++는 이 문자열에서 점프 테이블을 작성할 수 없습니다.
movf INDEX,W ; move the index value into the W (working) register from memory
addwf PCL,F ; add it to the program counter. each PIC instruction is one byte
; so there is no need to perform any multiplication.
; Most architectures will transform the index in some way before
; adding it to the program counter
table ; the branch table begins here with this label
goto index_zero ; each of these goto instructions is an unconditional branch
goto index_one ; of code
goto index_two
goto index_three
index_zero
; code is added here to perform whatever action is required when INDEX = zero
return
index_one
...
(wikipedia https://en.wikipedia.org/wiki/Branch_table) 코드)
대부분의 경우 문자열에서 첫 번째 문자를 꺼내고 해당 문자를 켜면 추가 작업을 수행할 수 있습니다.케이스가 같은 값으로 시작되면는 charat(1)에서 네스트된 스위치를 실행해야 할 수 있습니다.하지만 당신의 코드를 읽는 사람이라면 힌트를 주면 고맙겠지만, 왜냐하면 대부분의 사람들은 단지 만약-만일-만-만-만-만-만-만-만-만-만-만
스위치는 일체형(int, char, bool 등)에서만 동작합니다.맵을 사용하여 문자열을 번호와 페어링하고 그 번호를 스위치와 함께 사용하면 어떨까요?
언급URL : https://stackoverflow.com/questions/650162/why-cant-the-switch-statement-be-applied-to-strings
'programing' 카테고리의 다른 글
POI HSSF API를 사용하여 Excel 셀에서 날짜 값 읽기 (0) | 2023.04.15 |
---|---|
목록에 개체가 이미 있는지 확인하는 방법 (0) | 2023.04.15 |
이동 중에도 변수를 유지할 수 있는 방법이 있습니까? (0) | 2023.04.15 |
"변경 내용을 커밋하거나 병합하기 전에 저장하십시오"라는 git을 해결하려면 어떻게 해야 합니까? (0) | 2023.04.15 |
jQuery가 요청 본문에 유효한 json을 게시합니다. (0) | 2023.04.05 |