C++用户定义字面量

Share on:
400 Words | Read in about 2 Min | View times

Overview

C++11引入了用户自定义字面量(user-defined literal),也叫自定义后缀操作符。通过实现一个后缀操作符,可以将声明了后缀标识的字面量转换为所需的类型和数值。本节内容我们将探讨各种常见的字面量,以及C++11新加入的用户定义字面量。

本系列文章将包括以下领域:

本章其他内容请见 《现代C++》

字面量

维基百科对于字面量(literal)的定义如下:

字面常量(literal constant),是C程序设计语言与C++语言的词法上的概念(lexical conventions),是指源程序中表示固定值的符号。

我们一般把字面常量简称为字面量。现代C++支持的字面量类型包括:

整型字面量

支持的数值进制有10进制(如1234)、8进制(如0373)、16进制(如0x2f)、2进制(如0b1001)。

整型字面量按照以下顺序转化为整数类型表示:intunsigned intlongunsigned longlong longunsigned long long。其中,10进制表示的字面量仅考虑有符号的整数类型;而8进制或16进制的字面量先考虑能否用有符号的整数类型表示,如不能再考虑能否用同样长度的无符号整数类型表示。

例如,字面量0x87654321,这是一个正值,用4字节长的int无法表示,编译器就会自动选用unsigned int来表示该字面量。

整型字面量可以使用后缀uUulULullULL明确表示各种无符号整型,如1234u123ul

浮点型字面量

支持的数值进制有10进制(如1.5e152.3f)、16进制(如0x1p100x1.001p10)。如果不是科学计数法的形式,就必须有小数点,小数点前或后的数字可以省略。

没有后缀的浮点型字面量是double类型,使用后缀f的是float类型。

字符型字面量

字符型字面量是用单引号括起来的一个或多个字符。

没有前缀字符的叫普通字符型字面量或窄字符字面量,包含多个字符的普通字符字面量叫多字符字面量。

1//多字符字面量
2int a = 'abcd'; //值为0x61626364的整型数

在C++中,字符字面量achar类型,所以sizeof('a')1;而在C语言中,字符字面量aint类型,所以sizeof('a')4

当字符字面量有前缀uUL时,分别表示char16_tchar32_twchar_t

转义字符也是字符型字面量的一种,其表示范围可能不属于C++的字符范围,所以需要用反斜杠\来转义所需的字符。

字符串字面量

字符串字面量是最常见的形式。C++11加入了原始字符串字面量,来避免使用大量反斜杠转义字符,增强可读性和计算性能。

枚举字面量

枚举类型本质上是取有限个值的整型,在传统C++中,枚举字面量的作用域属于定义了这个枚举类型的作用域,而不属于这个枚举类型的内部,这容易导致枚举字面量重定义冲突:

1enum FileAccess {
2    Read,
3    Write,
4};
5
6enum FileShare {
7    Read, //error: ‘Read’ conflicts with a previous declaration
8    Write, //error: ‘Write’ conflicts with a previous declaration
9};

C++11为了解决这个问题,引入了枚举类。定义枚举类时在原enum关键字后面增加class关键字,此时枚举字面量的作用域就归属于这个枚举类了,避免了重定义冲突;同时也避免了相同数值的枚举字面量进行隐式转换,保证枚举是强类型的。

1enum class FileAccess {
2    Read,
3    Write,
4};
5
6enum class FileShare {
7    Read, //ok
8    Write, //ok
9};

C++11还支持指定枚举类的存储类型,从而可以支持前置声明:

1//前置声明
2enum class FileAccess : char;
3
4//枚举类定义
5enum class FileAccess : char {
6    Read,
7    Write,
8};

布尔型字面量

有两个布尔型字面量,即truefalse

指针型字面量

C++11定义了一个指针型字面量,即nullptr

用户自定义的字面量

用户定义字面量,user-defined literal,是C++11新加入的特性。

用户定义字面量

C++11允许我们定义一个用户定义后缀(user-defined suffix)来把原生的整型、浮点型、字符、字符串等字面量转换为我们自己需要的形式,这个形式可以是原生数据类型,也可以是自定义类。

具体地,字面量会自动调用一个函数来转换它的类型。由用户定义的字面量调用的函数称为字面量运算符。如果它是模板,则称为字面量运算符模板。

  • 字面量运算符

语法定义如下:

1return_type operator"" custom_suffix (parameters);
  • 字面量运算符模板

语法定义如下:

1template <char...> return_type operator"" custom_suffix ();

注意这两种语法定义中的自定义后缀必须以下划线_开头,并符合标识符命名规则。

另外,字面运算符的返回值类型无限制,但是参数只能是以下类型:

支持的参数类型 说明
const char* 原始字符串字面量,整型、浮点用户定义字面量的候选方式
unsigned long long int 整型用户定义字面量的首选方式
long double 浮点型用户定义字面量的首选方式
char
wchar_t
char8_t
char16_t
char32_t
const char*, size_t
const wchar_t*, size_t
const char8_t*, size_t
const char16_t*, size_t
const char32_t*, size_t

另外字面量运算符和字面量运算符模板是普通函数和模板函数,它们可以声明为inlineconstexpr

1constexpr long double operator"" _mm(long double x) { return x; }
2constexpr long double operator"" _cm(long double x) { return x * 10; }
3constexpr long double operator"" _m(long double x) { return x * 1000; }
4
5auto length = 100_mm; //100.0
6auto height = 3.0_cm; //30.0
7auto width = 1.5_m; //1500.0

再看另一个例子,用户定义字面量转换为自定义类型:

 1struct S {
 2    int value;
 3    void print() { std::cout << "value = " << value << std::endl; }
 4};
 5
 6S operator""_zxl(unsigned long long v) {
 7    S s;
 8    s.value = (int)v;
 9    return s;
10}
11
12int main() {
13    //2022_zxl.print(); //error: unable to find numeric literal operator ‘operator""_zxl.print’
14    2022_zxl .print(); //value = 2022
15    (2022_zxl).print(); //value = 2022
16    return 0;
17}

注意这里2022_zxl转换为S类型的对象后,要调用对象的成员函数,要么后面接空格,要么用圆括号把用户自定义字面量包裹起来。否则将被编译器当作_zxl.print()操作符来看待,而编译器找不到这样的重载操作符,所以会报错。

Prev Post: 『C++原始字符串字面量』
Next Post: 『C++ auto关键字』