zoukankan      html  css  js  c++  java
  • C++11 用户自定义字面量

    【1】为什么引入用户自定义字面量?

    在C/C++程序中,常常会使用结构体或者类来创造新的类型,以满足实际的需求。

    比如,在进行科学计算时,用户可能需要用到复数(通常会包含实部和虚部两部分)。

    对于颜色,用户通常会需要一个四元组(三原色及Alpha)。

    对于奥运会组委会,他们则常常会需要七元组(标示来自七大洲的状况)等等。

    而当用户想声明一个自定义类型的“字面量”(literal)时,尤其显得麻烦。如下示例:

     1 #include <iostream>
     2 using namespace std;
     3 
     4 typedef unsigned char uint8;
     5 
     6 struct RGBA
     7 {
     8     uint8 r;
     9     uint8 g;
    10     uint8 b;
    11     uint8 a;
    12     RGBA(uint8 R, uint8 G, uint8 B, uint8 A = 0)
    13         : r(R), g(G), b(B), a(A)
    14     {}
    15 };
    16 
    17 ostream & operator<<(ostream& out, RGBA& col)
    18 {
    19     return out << "r: " << (int)col.r
    20         << ", g: " << (int)col.g
    21         << ", b: " << (int)col.b
    22         << ", a: " << (int)col.a << endl;
    23 }
    24 
    25 void blend(RGBA& col1, RGBA& col2)
    26 {
    27     cout << "blend " << endl << col1 << col2 << endl;
    28 }
    29 
    30 int main()
    31 {
    32     RGBA col1(255, 240, 155);
    33     RGBA col2({ 15, 255, 10, 7 });
    34     blend(col1, col2);
    35     system("pause");
    36 }
    37 
    38 /*运行结果
    39 blend
    40 r: 255, g: 240, b: 155, a: 0
    41 r: 15, g: 255, b: 10, a: 7
    42 */

    在程序中想通过blend函数对两个确定的RGBA对象进行运算。采用了传统的方式,即先声明两个RGBA的变量,并且赋予相应初值,再将其传给函数blend。

    在编写测试用例的时候,常会遇到需要声明较多值确定的RGBA变量。那么这样的声明变量–传值运算的方式显得非常麻烦。

    如果自定义类型可以像内置类型一样向函数传递字面常量,比如向函数func传递字面常量func(2, 5.0f),无疑这样的测试代码会方便很多。

    以上即引入用户自定义字面量的原因。

    【2】自定义字面量的应用与注意事项

    C++11实现了如上的愿望,即可以通过定一个后缀标识的操作符,将声明了该后缀标识的字面量转换为需要的相应类型。

    如上示例改造代码:

     1 #include <iostream>
     2 using namespace std;
     3 
     4 typedef unsigned char uint8;
     5 
     6 struct RGBA
     7 {
     8     uint8 r;
     9     uint8 g;
    10     uint8 b;
    11     uint8 a;
    12     RGBA(uint8 R, uint8 G, uint8 B, uint8 A = 0)
    13         : r(R), g(G), b(B), a(A)
    14     {}
    15 };
    16 
    17 RGBA operator "" _C(const char* col, size_t n)
    18 {
    19     const char* p = col;
    20     const char* end = col + n;
    21     const char* r, *g, *b, *a;
    22     r = g = b = a = nullptr;
    23     for (; p != end; ++p)
    24     {
    25         if (*p == 'r') r = p;
    26         else if (*p == 'g') g = p;
    27         else if (*p == 'b') b = p;
    28         else if (*p == 'a') a = p;
    29     }
    30     if ((nullptr == r) || (nullptr == g) || (nullptr == b))
    31     {
    32         throw;
    33     }
    34     else if (nullptr == a)
    35         return RGBA(atoi(r + 1), atoi(g + 1), atoi(b + 1));
    36     else
    37         return RGBA(atoi(r + 1), atoi(g + 1), atoi(b + 1), atoi(a + 1));
    38 }
    39 
    40 ostream& operator<<(ostream& out, RGBA& col)
    41 {
    42     return out << "r: " << (int)col.r
    43         << ", g: " << (int)col.g
    44         << ", b: " << (int)col.b
    45         << ", a: " << (int)col.a << endl;
    46 }
    47 
    48 void blend(RGBA && col1, RGBA && col2)
    49 {
    50     cout << "blend " << endl << col1 << col2 << endl;
    51 }
    52 
    53 int main()
    54 {    
    55     blend("r255 g240 b155"_C, "r15 g255 b10 a7"_C);
    56     system("pause");
    57 }
    58 
    59 /*运行结果
    60 blend
    61 r: 255, g: 240, b: 155, a: 0
    62 r: 15, g: 255, b: 10, a: 7
    63 */

    声明了一个字面量操作符(literal operator)函数:RGBA operator"" _C(const char*col, size_t n)函数。

    这个函数会解析以_C为后缀的字符串,并返回一个RGBA的临时变量。

    有了这样一个用户字面常量的定义,程序中不再需要通过声明RGBA类型的声明变量–传值运算的方式来传递实际意义上的常量。

    通过声明一个字符串以及一个_C后缀,operator""_C函数会产生临时变量。blend函数就可以通过右值引用获得这些临时值并进行计算。

    这样一来,用户就完成了定义自定义类型的字面常量,可见main函数中的代码书写显得更加清晰。

    注意事项:

    (1)在字面量操作符函数的声明中,operator""与用户自定义后缀之间必须有空格。

    (2)后缀建议以下划线开始。不宜使用非下划线后缀的用户自定义字符串常量,否则会被编译器警告。

    当然,这也很好理解,因为形如201203L这样的字面量,后缀“L”无疑会引起一些混乱的状况。为了避免混乱,最好只使用下划线开始的后缀名。

    (3)C++11标准中要求声明字面量操作符有一定的规则,该规则跟字面量的“类型”密切相关。具体规则如下:

    [1] 如果字面量为整型数,那么字面量操作符函数只可接受unsigned long long或者const char*为其参数。

    当unsigned long long无法容纳该字面量的时候,编译器会自动将该字面量转化为以为结束符的字符串,并调用以const char*为参数的版本进行处理。

    [2] 如果字面量为浮点型数,则字面量操作符函数只可接受long double或者const char*为参数。const char*版本的调用规则同整型的一样(过长则使用const char*版本)。

    [3] 如果字面量为字符串,则字面量操作符函数函数只可接受const char*, size_t为参数(已知长度的字符串)。

    [4] 如果字面量为字符,则字面量操作符函数只可接受一个char为参数。

    如下应用示例:

     1 #include <iostream>
     2 using namespace std;
     3 
     4 // 浮点数操作符
     5 long double operator"" _mm(long double x) { return x / 1000; }
     6 long double operator"" _m(long double x) { return x; }
     7 long double operator"" _km(long double x) { return x * 1000; }
     8 
     9 // 字符串操作符,第二个参数会自动推断为字符串的长度
    10 size_t operator"" _len(char const*, size_t n)
    11 {
    12     return n;
    13 }
    14 
    15 // 原始字面量操作符1
    16 char const* operator"" _r(char const* s)
    17 {
    18     return s;
    19 }
    20 
    21 // 原始字面量操作符2
    22 string operator"" _rs(char const* s)
    23 {
    24     return 'x' + string(s) + 'y';
    25 }
    26 
    27 int main()
    28 {
    29     cout << 1.0_mm << '
    ';  // 0.001
    30     cout << 1.0_m << '
    ';   // 1
    31     cout << 1.0_km << '
    ';  // 1000
    32 
    33     cout << "ABCDEFGH"_len << '
    '; // 8
    34 
    35     cout << 12_r << '
    '; // 12
    36     cout << 5_rs << '
    '; // x5y
    37 }

    5~7行:很简单不做解释。

    10~13行:对于字符串相当有用,因为第二个参数会自动推断为字符串的长度。

    16~19行:为原始字面量raw literal操作符。

    22~25行:字面量的返回值没有被严格限定,完全可以提供相容类型的返回值。

    good good study, day day up.

    顺序 选择 循环 总结

  • 相关阅读:
    javascript往textarea追加内容
    Java之Comparable接口和Comparator接口
    Java之iterator迭代器和iterable接口
    Java之hashSet实现引用类型的禁止重复功能
    Java之获取系统属性
    Java之String,StringBuffer,StringBuilder类
    Java之sleep和wait的区别
    Android开发之ADT中无Annotation Processin的解决办法
    Android开发之注解式框架ButterKnife在ADT中的设置
    Android开发UI之Action Bar
  • 原文地址:https://www.cnblogs.com/Braveliu/p/12241837.html
Copyright © 2011-2022 走看看