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

1.13 使用结构化绑定处理多值返回

函数返回多个值的情况非常常见,但是C++中没有一流的解决方案可以直接实现这一点。开发人员必须做出选择:通过引用函数形参返回多个值,或者定义一个包含多值的结构,或者返回std::pair或std::tuple。前两个使用命名变量,它们的优点是清楚地指明返回值的含义,缺点是必须显式地定义它们。std::pair的成员为first和second,而std::tuple的匿名成员只能通过函数调用访问,但可以使用std::tie()复制到命名变量。不过,这些解决方案都不是很理想。

C++17将std::tie()的语义扩展为一级核心语言特性,支持将元组的值解包到命名变量中。这个特性称为 结构化绑定

1.13.1 准备工作

对于本节内容,你应该熟悉标准实用程序类型std::pair和std::tuple以及实用函数std::tie()。

1.13.2 使用方式

要支持C++17的编译器从函数返回多个值,应该执行以下操作:

1)使用std::tuple作为返回类型:

2)使用结构化绑定将元组的值解包到命名对象中:

3)使用分解声明将返回值绑定到if语句或switch语句中的变量:

1.13.3 工作原理

结构化绑定是一种语言特性,它的工作原理与std::tie()类似,只不过我们不必为每个需要用std::tie()显式解包的值定义命名变量。在结构化绑定中,我们使用auto标识符在单个定义中定义所有命名变量,以便编译器可以推断每个变量的正确类型。

为了举例说明这一点,我们考虑向std::map中插入项的情况。insert方法返回一个std::pair,其中包含已插入元素或阻止插入的元素的迭代器,以及一个指示插入是否成功的布尔值。下面所示的代码非常明确,但是使用second或first->second使代码很难阅读,因为你需要不断地弄清楚它们代表什么:

使用std::tie可以使上述代码更具可读性,它将元组解包为单个对象(并与std::pair一起使用,因为std::tuple有一个来自std::pair的转换赋值):

代码并不一定更简单,因为它需要提前定义解包到的对象。类似地,元组的元素越多,需要定义的对象就越多,但是使用命名对象使代码更容易阅读。

C++17的结构化绑定将元组元素解包到命名对象中,该特性非常重要。它不需要使用std::tie(),并且对象在声明时被初始化:

在前面的例子中,使用多个块是必要的,因为变量不能在同一个块中重新声明,结构化绑定意味着使用auto标识符进行声明。因此,如果需要像前面的示例那样进行多个调用,并使用结构化绑定,则必须使用不同的变量名或多个块。另一种选择是不使用结构化绑定并使用std::tie(),因为它可以用相同的变量调用多次,所以只需要声明它们一次。

在C++17中,也可以在if和switch语句中以if(init; condition)和switch(init;condition)这种形式声明变量。这可以与结构化绑定结合,以生成更简单的代码。让我们看一个例子:

在前面的代码片段中,我们尝试向map中插入一个新值,结果被解包到两个变量:it和inserted,它们在初始化部分的if语句的作用域中定义。我们根据inserted变量的值计算if语句的条件。

1.13.4 更多

虽然我们关注的是将名称绑定到元组的元素,但结构化绑定可以在更广泛的范围内使用,因为它们还支持绑定到数组元素或类的数据成员。如果想绑定到数组的元素,则必须为数组的每个元素提供一个名称,否则声明格式不正确。下面是一个绑定到数组元素的例子:

在本例中,arr是一个包含两个元素的数组。我们首先将a和b绑定到它的元素,然后将x和y引用绑定到它的元素。对数组元素所做的更改对变量a和b是不可见的,但对x和y引用是可见的,详见将这些值输出到控制台的注释。这是因为在执行第一次绑定时创建了数组的副本并将a和b绑定到了该副本的元素。

正如我们已经提到的,还可以绑定到类的数据成员,适用的限制如下:

❍ 绑定只能用于类的非静态成员。

❍ 类不能有匿名的联合体(union)成员。

❍ 标识符的数量必须匹配类的非静态成员的数量。

标识符的绑定是按照数据成员声明的顺序进行的,其中可以包括位字段。下面是一个例子:

同样,对foo对象的更改对变量i和n是不可见的,但对ri和rn是可见的,这是因为结构化绑定中的每个标识符都成为引用类数据成员的左值的名称(就像对数组那样,它引用数组的一个元素)。但是,标识符的引用类型指向对应的数据成员(或数组元素)。

C++20标准对结构化绑定进行了一系列改进,包括:

❍ 在结构化绑定的声明中可以包含static或thread_local存储类说明符。

❍ 允许使用[[maybe_unused]]属性声明结构化绑定,一些编译器(如Clang和GCC)已经支持此特性。

❍ 允许在lambda中捕获结构化绑定标识符,所有标识符(包括那些绑定到位字段的标识符)都可以按值捕获。除了那些绑定到位字段的标识符外,所有标识符也可以通过引用捕获。

这些变化使我们能够编写以下内容:

这些示例展示了在C++20的lambda中捕获结构化绑定的各种方法。

1.13.5 延伸阅读

❍ 阅读1.1节,以了解C++中自动类型推导的工作原理。

❍ 阅读3.2节,以了解如何将lambda与标准库通用算法一起使用。

❍ 阅读4.6节,以了解如何使用标准属性为编译器提供提示。 CJKIZCKGz8y9whypYU8/JWmnEl7zICGLlpR08zCrDSRdAju/Oo89jwnjiE94Ua8y

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