购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.5 创建cooked的用户自定义字面量

字面量是内置类型(数值类型、布尔类型、字符类型、字符串类型和指针类型)的常量,不能在程序中更改。该术语定义了一系列前缀和后缀来指定字面量(前缀和后缀实际上是字面量的一部分)。C++11允许我们通过定义称为字面量操作符的函数(引入了指定字面量的后缀)来创建用户自定义字面量,这只适用于数值类型和字符串类型。

这为在未来版本中定义两个标准字面量提供了可能性,并允许开发人员创建他们自己的字面量。在本节中,我们将学习如何创建自己的cooked字面量。

2.5.1 准备工作

用户自定义的字面量有两种形式:原始形式和cooked形式。原始字面量不被编译器处理,而cooked字面量是被编译器处理过的值(示例包括处理字符串中的转义字符序列或从0xBAD中识别数值,如整数2989)。原始字面量只适用于整数类型和浮点类型,而cooked字面量也适用于字符类型和字符串字面量。

2.5.2 使用方式

要创建cooked用户自定义字面量,应该遵循以下步骤:

1)在单独的命名空间中定义字面量,以避免命名冲突。

2)在用户自定义的后缀前加上下划线(_)。

3)为cooked字面量定义以下形式之一的字面量操作符:

下面的示例为KB创建一个用户定义的字面量:

2.5.3 工作原理

当编译器遇到带有用户定义的后缀S(对于第三方后缀,总是有一个前导下划线,因为没有前导下划线的后缀是为标准库保留的)的用户自定义字面量时,它会进行非限定名称查找,以识别名称为operator ""S的函数。如果找到了,则根据字面量的类型和字面量操作符的类型调用它,否则编译器将产生一个错误。

在2.5.2节所示的例子中,字面量操作符被称为operator "" _KB,其参数类型为unsigned long long int,这是字面量操作符唯一能够处理的整数类型。类似地,对于用户自定义的浮点型字面量,参数类型必须是long double,因为对于数值类型,字面量操作符必须能够处理尽可能大的值。这个字面量操作符会返回一个constexpr值,以便在编译时其值可用,比如用于指定数组的大小,如前面的示例所示。

当编译器识别出用户自定义字面量,并且必须调用相应的用户定义的字面量操作符时,它将根据以下规则从重载函数集中选择重载的函数:

对于整型字面量 :按以下顺序调用,先选择接受unsigned long long的操作符,然后选择接受const char*的原始字面量操作符或字面量操作符模板。

对于浮点型字面量 :按以下顺序调用,先选择接受long double的操作符,然后选择接受const char*的原始字面量操作符或字面量操作符模板。

对于字符型字面量 :根据字符类型(char、wchar_t、char16_t和char32_t)调用合适的操作符。

对于字符串字面量 :根据字符串类型调用合适的操作符,该操作符接受指向字符串(字符及其大小)的指针。

在下面的例子中,我们将定义一个由单位和数量组成的系统。我们想使用千克、件、升和其他类型的单位。这在处理订单的系统中可能很有用,我们需要为每一件商品指定数量和单位。

我们在命名空间units中定义了以下内容:

❍ 可能类型的单位(千克、米、升和件)的作用域枚举:

❍ 指定特定单位数量(如3.5千克或42件)的类模板:

❍ 用于quantity类模板的operator+和operator-函数(以便能够加减数量):

❍ 创建quantity字面量的字面量操作符,定义在一个名为unit_literals的内部命名空间中(这样做的目的是避免与来自其他命名空间的字面量发生可能的命名冲突)。如果确实发生了这样的冲突,开发人员可以在需要定义字面量的作用域中使用适当的命名空间来定义它们:

仔细观察,便会发现前面定义的字面量操作符是不一样的:

❍ _kg主要针对整型和浮点型字面量定义,使我们能够创建整数值和浮点值,比如1_kg和1.0_kg。

❍ _l和_m只针对浮点型字面量定义,这意味着我们只能使用浮点数为这些单位定义数量字面量,例如4.5_l和10.0_m。

❍ _pcs只针对整型字面量定义,这意味我们只能为单位“件”定义整数数量,例如42_pcs。

有了这些字面量操作符,我们可以对各种数量进行操作,下面的示例显示了有效和无效的操作:

q1是1 kg,它是一个整数值。因为存在重载operator"" _kg(unsigned long long const)函数,整数1可以正确被创建成字面量。同样,q2是4.5 kg,它是一个实数。因为存在重载operator "" _kg (long double),所以双精度浮点值4.5也可以被正确创建成字面量。

q6是1L,因为没有重载operator "" _l(unsigned long long)函数,所以不能创建字面量。它需要一个接受unsigned long long的重载函数,但这样的重载函数不存在。类似地,q7是2.0件,但是“件”的字面量只能从整数值创建,因此,这会导致另一个编译错误。

2.5.4 更多

虽然从C++11开始就可以使用用户自定义字面量了,但是标准的字面量操作符从C++14标准才开始。进一步的标准用户自定义字面量已经添加到该标准的下一版中。下面列出来的这些都是标准的用户自定义字面量:

❍ operator""s用于定义std::basic_string字面量,operator""sv(C++17)用于定义std::basic_string_view字面量:

❍ operator""h、operator""min、operator""s、operator""ms、operator""us和operator""ns用于创建std::chrono::duration值:

❍ operator""y用于创建std::chrono::year字面量,operator""d用于创建std::chrono::day字面量,两者都已被添加到C++20:

❍ operator""if、operator""i和operator""il用于创建std::complex值:

标准的用户自定义字面量定义在多个命名空间中。例如,字符串的""s和""sv字面量定义在命名空间std::literals::string_literals中。

但是,literals和string_literals都是内联命名空间。因此,可以通过using namespace std::literals、using namespace std::string_literals或using namespace std::literals::string_literals来访问字面量。在前面的例子中,我们首选第二种形式。

2.5.5 延伸阅读

❍ 阅读2.7节,以了解如何定义字符串字面量而不需要转义特殊字符。

❍ 阅读2.6节,以了解如何提供输入序列的自定义解释,从而改变编译器的正常行为。

❍ 阅读1.12节,以了解如何使用内联命名空间和条件编译来管理源代码版本。 c9w79mBvyR7Hr8r6Da0jKbARRw2pUg54m8hMZwtQdGjbGqcIQcXRkhnY1C4RhBKp

点击中间区域
呼出菜单
上一章
目录
下一章
×