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

2.6 创建原始的用户自定义字面量

在前面的章节中,我们了解了C++11允许库实现者和开发人员创建用户自定义字面量的方式,以及C++14标准中的用户自定义字面量。然而,用户自定义字面量有两种形式:一种是cooked形式,其中字面量在提供给字面量操作符之前由编译器处理;另一种是原始形式,这种字面量在提供给字面量操作符之前不经过编译器处理。后者仅适用于整型和浮点型。原始字面量对于改变编译器的正常行为很有用。例如,像3.1415926这样的序列被编译器解释为浮点值,但是通过使用原始的用户自定义字面量,它可以被解释为用户自定义的十进制值。在本节中,我们将研究如何创建原始的用户自定义字面量。

2.6.1 准备工作

在开始本节之前,强烈建议先浏览一下前一节的内容,因为这里不再赘述用户自定义字面量的细节。

为了举例说明创建原始用户自定义字面量的方法,我们将定义二进制字面量。这些二进制字面量可以是8位、16位以及32位(无符号)类型,这些类型将被称为byte8、byte16和byte32,我们将创建的字面量称为_b8、_b16和_b32。

2.6.2 使用方式

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

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

2)总是在已使用定义的后缀前加上一个下划线(_)。

3)定义以下形式的字面量操作符或字面量操作符模板:

下面的例子展示了8位、16位和32位二进制字面量可能的实现方式:

2.6.3 工作原理

首先,我们在名为binary的命名空间中定义所有内容,引入几个类型别名:它们分别是byte8、byte16和byte32,顾名思义,它们分别表示8位、16位和32位的整数类型。

上一节中的实现使我们能够定义形式为1010_b8(byte 8值,换算成十进制为10)或000010101100_b16(byte 16值,换算成十进制为2130496)的二进制字面量。但是,我们希望确保不超过每种类型的位数,换句话说,像111100001_b8这样的值应该是非法的,编译器会产生一个错误。

字面量操作符模板定义在名为binary_literal_internals的嵌套命名空间中。对于避免与来自其他命名空间的其他字面量操作符发生命名冲突,这是一种很好的实践。如果发生类似的情况,你可以选择在正确的作用域中使用适当的命名空间(例如在函数或代码块中使用某个命名空间,在另一个函数或代码块中使用另一个命名空间)。

这三个字面量操作符模板非常相似,唯一不同的是它们的名称(_b8、_16和_b32)、返回类型(byte8、byte16和byte32)以及静态断言中检查数字位数的条件。

我们将在后面的小节中探讨可变参数模板和模板递归的细节,但是,为了更好地理解,这里先给出具体的实现方式:bits是一个模板参数包,它不是单个值,而是模板可以实例化的所有值。例如,如果我们考虑字面量1010_b8,那么字面量操作符模板将被实例化为operator"" _b8<'1','0','1','0'>()。在继续计算二进制值之前,我们首先检查字面量中值的位数。对于_b8,这个值不能超过8位(包括后面的零);类似地,_b16最多为16位,_b32最多为32位。为此,我们使用sizeof...操作符,它返回参数包(在本例中为bits)中的元素个数。

如果字面量值的位数是正确的,则可以继续展开参数包并递归计算由二进制字面量表示的十进制值,这是在附加类模板及其特化的帮助下完成的,这些模板定义在另一个名为binary_literals_internals的嵌套命名空间中。这也是一个很好的实践,因为它对客户端隐藏了实现细节(没有适当的限定)(除非显式的using namespace指令使它们对当前的命名空间可用)。

尽管这看起来像递归,但它并不是真正的运行时递归。这是因为在编译器展开并从模板生成代码之后,我们最终基本上就是在调用带有不同数量参数的重载函数。我们稍后将在3.5节对此进行解释。

类模板binary_struct有一个用于函数返回类型的模板类型CharT(之所以需要这个,是因为字面量操作符模板应该返回byte8、byte16或byte32)和一个参数包:

通过参数包分解,可以获得该类模板的几种特化。当包的第一个数字为'0'时,计算值保持不变,然后继续展开包的其余部分;如果包的第一个数字是'1',则新值为1,并随着包剩余位的位数或包剩余位的值向左移动:

最后一个特化涵盖了包为空的情况,对于这种情况,我们返回0:

在定义了这些辅助类之后,我们就可以按预期实现byte8、byte16和byte32的二进制字面量。值得注意的是,我们需要将命名空间binary_literals的内容带入当前命名空间,才能使用字面量操作符模板:

以下定义会触发编译错误:

这是因为没有满足static_assert中的条件,在所有的例子中,字面量操作符前面的字符序列的长度都大于预期的长度。

2.6.4 延伸阅读

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

❍ 阅读2.5节,以了解如何创建用户自定义类型的字面量。

❍ 阅读3.5节,以了解可变参数模板如何使我们能够编写可以接受任意数量参数的函数。

❍ 阅读1.2节,以了解类型的别名。 6wYY38GkJYWbJKF4OfVcfo1N2QQqLu7J+jFu8hTi6cAeZK/mG8f3rTdbILLFWKHG

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

打开