使用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;
	case "third"_hash:
		cout << "3rd one" << endl;
		break;
	default:
		cout << "Default..." << endl;
	}
}

 

————————————————
版權聲明:本文爲CSDN博主「YoziDream」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/yozidream/article/details/22789147

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章