C++11新特性,實現用字串作為switch的case子句
有時候,我們想寫出下面這樣的switch語句:
const char *
str = "first" ;
switch (str){
case "first" :
cout
<< "1st
one" <<
endl;
break ;
case "second" :
cout
<< "2nd
one" <<
endl;
break ;
case "third" :
cout
<< "3rd
one" <<
endl;
break ;
default :
cout
<< "Default..." <<
endl;
}
|
但是在c++中,是不能用字串來作為case的標籤的;於是,很疑惑,我們只能用其他的辦法來實現這樣的需求。
但幸運的是,c++11引入了constexpr和自定義文字常量,將這兩個新特性結合,我們實現出看上去像上面這樣的程式碼。
基本思路為:
1、定義一個hash函式,計算出字串的hash值,將字串轉換為1個整數;
typedef std::uint64_t
hash_t;
constexpr
hash_t prime = 0x100000001B3ull;
constexpr
hash_t basis = 0xCBF29CE484222325ull; hash_t
hash_( char const *
str)
{
hash_t
ret{basis};
while (*str){
ret
^= *str;
ret
*= prime;
str++;
}
return ret;
}
|
2、利用c++11自定義文字常量的語法,定義一個constexpr函式,switch的case標籤處呼叫這個constexpr函式。如下
constexpr
hash_t hash_compile_time( char const *
str, hash_t last_value = basis) {
return *str
? hash_compile_time(str+1, (*str ^ last_value) * prime) : last_value;
}
|
這個函式只有短短的一行,利用遞迴得到了與上面hash_函式得到的同樣值,由於用constexpr聲明瞭函式,因此編譯器可以在編譯期得出一個字串的hash值
而這正是關鍵,既然是編譯器就可以得到的整型常量,自然可以放到switch的case標籤處了。
於是我們可以寫出這樣的swich語句:
void simple_switch2( char const *
str)
{
using namespace std;
switch (hash_(str)){
case hash_compile_time( "first" ):
cout
<< "1st
one" <<
endl;
break ;
case hash_compile_time( "second" ):
cout
<< "2nd
one" <<
endl;
break ;
case hash_compile_time( "third" ):
cout
<< "3rd
one" <<
endl;
break ;
default :
cout
<< "Default..." <<
endl;
}
}
|
這個實現中,hash_compile_time("first")是編譯器計算出來的一個常量,因此可以用作case標籤;而且如果出現了hash值衝突,編譯器回給出錯誤提示。
3、上面的語法還不夠漂亮,利用自定義文字常量,過載一個operator如下:
constexpr
unsigned long long operator "" _hash( char const *
p, size_t )
{
return hash_compile_time(p);
}
|
現在我們可以寫這樣的文字常量,用“_hash”作為自定義文字常量的字尾,編譯器看到這個字尾結尾的文字常量,就會去呼叫我們過載的operator,得到和呼叫hash_compile_time是一樣的值,但看起來舒服多了:
"first" _hash
|
現在,我們寫出的switch語句就好看多了。
void simple_switch( char const *
str)
{
using namespace std;
switch (hash_(str)){
case "first" _hash:
cout
<< "1st
one" <<
endl;
break ;
case "second" _hash:
cout
<< "2nd
one" <<
endl;
break ;
|