编程世界无奇不有,很多人肯定在C++代码中看到过如下所示的东西:
double area = 80_rr;
或者有类似这种的
String str = arg;
Switch(str.c_str())
{
Case:...
}
等类似的东西,what? C++ 中switch还可以直接对字符串进行判断?我越来越好奇了,这些东西如何来的?下面就来解开C++的神奇面纱,C++ 11特性向上兼容 ---用户定义字面量运算符
规范自带的float m = 4.0f; 后面的f是字面量运算符,现在实现如规范一样的东西,请继续往下看。
用户定义字面量运算符
用户定义字面量所调用的函数被称为字面量运算符(或若它是模板,则被称为字面量运算符模板)。
C++ 11的形式为
Operator “” name
C++14的形式为
Operator “”name
这里特别指出:
用户自定义字面量运算符的参数(返回值随意),函数的参数必须是以下的类型
1、const char *
2、unsigned long long int
3、long double
4、char
5、wchar_t
6、char16_t
7、char32_t
8、const char * , std::size_t
9、const wchar_t * , std::size_t
10、 const char16_t * , std::size_t
11、const char32_t * , std::size_t
后四个多了一个size_t的参数:其中字符串是无ud-suffix 的字面量,且size_t 则是其排除空终止字符的长度(这里官方的解释,搞那么深奥,说简单点就是你传入的字符串的长度,比如你的字符串是”hello”,那么后面的size就等价于strlen(“hell”),好理解多了吧)
注意:
constexpr size_t operator"" _hash2(char const*p)
{
return strlen(p);
}
constexpr size_t operator "" _hash1(char const*p, size_t len)
{
return len;
}
很多人会有疑问,为何有了上面的1的参数格式,还要多一个8这种两个参数的格式呢?
这里就解释一下吧,前者适用于123_hash2 这种格式的字面量,后者适用于”123”_hash1 这种格式,是不是看出不同了呢?如果有其他使用格式,希望回复我哦,我会去一一给大家一个使用的测试的。
C++14简单的看起来就是C++11“”后面必须带空格,C++14里面就可以省去空格写在一起。
下面简单的举一个例子:
constexpr long double operator"" _v( long double deg )
{
return deg*3.141592;
}
那么你在函数调用时,可以定义:
double are = 1.0_v;
输出are的值即为:3.141592
看了这个例子基本知道用户自定义字面量运算符的作用了,即在程序编译的时候,将1.0_v通过上面定义的函数得出一个右值,然后赋值给are。即你可以将1.0_v也看成一个常量值。
但是请注意,C++11 或C++14等用户自定义的字面量运算符必须以_(下划线开头)如上面的_v,或许是为了避免和标准头文件里面的 不带下划线的字面量常量发生冲突,不然我真想不出C++14里面都有operator""if operator""i operator""il 这种形式的字面量常量定义,为何用户不能必须使用_下划线了。
如果你看到这里了,那么就很好理解了,为何switch可以对字符串进行case比较了,因为case后面的值不能是字符串,但是有了用户自定义字面量运算符,那么用户自己完全可以将字符串转换成整形数据即可了。比如下面这个例子:
很多人想在C++中实现如下功能的switch:
const char* str = "first";
switch(str){
case "first":
cout << "1st" << endl;
break;
case "second":
cout << "2nd" << endl;
break;
case "third":
cout << "3rd" << endl;
break;
}
但是直接写肯定是编译不过的,那么现在利用C++11中的用户自定义字面量运算符来实现上面想要实现的switch功能吧:
//这里是定义一个hash序列,
using namespace std;
typedef std::uint64_t hash_t;
constexpr hash_t prime = 0x100000001B3ull;
constexpr hash_t basis = 0xCBF29CE484222325ull;
hash_t hash_(char const*str) //功能和hash_compile_time功能相同,处理阶段不同,一个是运行时没,一个是编译时
{
hash_t ret{ basis };
while (*str) {
ret ^= *str;
ret *= prime;
str++;
}
return ret;
}
//下面定义的预处理函数,使得和上面的hash_值得到的结果一样,因为字串需要调用hash_函数将字串转化成hash序列,hash_compile_time函数在预编译时也能达到相同值,所以两个函数是一一对应的
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 unsigned long long operator "" _hash(char const*p, size_t)
{
return hash_compile_time(p);
}
void simple_switch(char const*str)
{
using namespace std;
switch (hash_(str)) {
case "first"_hash:
cout << "1st" << endl;
break;
case "second"_hash:
cout << "2nd" << endl;
break;
case "third"_hash:
cout << "3rd" << endl;
break;
}
}
int main()
{
simple_switch("first");
system("pause");
}
输出结果即为 1st,上面switch部分代码截取自http://blog.csdn.net/yozidream/article/details/22789147的博客。
怎么样,各位小伙伴,是不是看了用户字面量运算符后,感觉是不是很舒服,很多程序可以将处理定义在编译阶段,能够提高程序运行时的效率,但是上面的switch显然有大量的hash运算,并不是最优的解决方案,因为你完全可以用map表直接取值就好了,使用只是为了给大家更为贴切的讲述这个原理噶。
可能大家对于模板特化时求累积等编译时得出系统允许的栈长度,但是这和用户字面量运算符有一定的差异。需要了解更多请关注 丁丁猫编程 D-CAT 里面会有大量技术性文章分阶段分享给大家。
丁丁猫编程 D-CAT 丁丁猫编程 D-CAT 丁丁猫编程 D-CAT 微信的哦