从原理上讲,结构化绑定可用于拥有public成员变量的结构体,原始的C风格数组,以及类Tuple的对象:
- 对于所有非静态(static)的成员变量都是public的结构体和类,可以将每个非静态成员变量绑定到一个确切的名字上。
- 对于原始数组,可以将数组的每个元素绑定到一个确切的名字上。
- 对于任意类型,可以使用类Tuple的API来绑定名字,API定义其中的元素。API大体上需要为类型type定义如下组件:
- std::tuple_size::value要返回元素个数。
- std::tuple_element::type返回第idx个元素的类型。
- 全局或成员函数get()返回第idx个元素的值。
标准库中的类型std::pair, std::tuple, and std::array就是典型的提供了这些API的例子。
如果结构体或类提供了类Tuple的API,这些API就会被使用。
所有应用场景中,元素或成员变量个数要跟声明的结构化绑定的名字个数一致。不能跳过名字,也不能一个名字使用2次。然而,可以使用非常短的名字,比如‘_’(有些程序员喜欢,有些不喜欢并且不允许在全局名字空间内使用),但是这在同一个可见域中只能用一次:
- auto [_,val1] = getStruct(); // OK
复制代码- auto [_,val2] = getStruct(); // 错误: 名字 _ 已经用过
复制代码 同时,也不支持嵌套。
下面将仔细介绍一下这些结构化绑定的用法。
前面的示例中已经有过简单的描述了拥有public成员变量的结构体和类如何使用结构化绑定。典型的应用是在一个数据结构中直接使用多个返回值。但是要注意一些边界情况。
注意,继承的时候可能有使用限制。所有非静态成员变量必须是同一个类定义的成员(因此,它们必须是某个类型的成员,或同一个无歧义的public基类的成员):
- auto [x, y] = D1{}; // OK
复制代码- [/code][code]struct D2 : B {
复制代码- auto [i, j, k] = D2{}; // 编译错误
复制代码 要注意的是,只有public成员变量的顺序是稳定的才能使用结构化绑定。否则,如果B里的int a和int b顺序变了,x和y就得到不同的值了。为了支持这种稳定性,C++17为一些标准库中的结构体定义了成员的顺序(以后也许会讲到)。
联合体(union)不允许结构化绑定。
下面的代码以原始C风格数组的2个元素初始化了x和y:
- auto [x, y] = arr; // x 和 y 是数组arr的一份拷贝的int元素
复制代码- auto
从原理上讲,结构化绑定可用于拥有public成员变量的结构体,原始的C风格数组,以及类Tuple的对象:
对于所有非静态(static)的成员变量都是public的结构体和类,可以将每个非静态成员变量绑定到一个确切的名字上。
对于原始数组,可以将数组的每个元素绑定到一个确切的名字上。
对于任意类型,可以使用类Tuple的API来绑定名 ...查看全文
软件开发谈 发表于 2019-7-28 23:29
= arr; // 错误: 元素个数不一致 复制代码只有当数组大小已知的情况下才能用。如果数组作为参数传递,就不能用了,因为它的类型会衰减为指针。
C++允许返回已知大小的数组的引用,所以这个特性可用于函数返回一个数组,且知道其大小:
- auto getArr() -> int(&)[2]; // getArr() 返回原始int 数组的引用
复制代码- auto [x, y] = getArr(); // x 和y 是返回的数组arr的一份拷贝中的int元素
复制代码 也可以将结构化绑定用于std::array,它提供了类Tuple的API,后面会介绍。
- std::pair, std::tuple以及std::array
结构化绑定机制是可扩展的,因此可以给任意类型增加结构化绑定支持。标准库就将此特性应用到std::pair,std::tuple和std::array。
std::array
例如,下面的代码初始化了a,b,c和d作为getArray()返回的std::array的一份拷贝的4个元素的名字:
- auto [a,b,c,d] = getArray(); // a,b,c,d是返回值的另一份拷贝的4个元素的名字
复制代码 这里a,b,c和d是getArray()返回的std::array的一份拷贝的元素的结构化绑定。
写入操作也是支持的,只需将非临时返回值作为初始化的值即可。例如:
- std::array stdarr { 1, 2, 3, 4 };
复制代码- auto& [a,b,c,d] = stdarr;
复制代码 std::tuple
下面的代码初始化了a,b和c作为getTuple()返回的std::tuple的一份拷贝的3个元素的别名:
- auto [a,b,c] = getTuple(); // a,b,c的类型和值是返回的tuple的3个元素的类型和值
复制代码 也就是说,a的类型是char,b的类型是float,c的类型是std::string。
std::pair
作为另一个例子,在关联容器或无序容器上
调用insert()函数,处理其返回值的代码,可以通过避免使用更泛化的名字first和second使得可读性更高:
- auto ret = coll.insert({"new",42});
复制代码- // 如果插入失败,则使用迭代器 ret.first 处理错误
复制代码 绑定名字可以在语义上更好地表达它们的目的:
- auto [pos,ok] = coll.insert({"new",42});
复制代码- // 如果插入失败,则使用迭代器 pos处理错误:
复制代码 要注意到的是,这个特殊的例子在C++17中提供了“带初始化的if”来更好地解决。
给pair和tuple的结构化绑定赋新值
声明结构化绑定后,通常不能再修改所有绑定,因为结构化绑定只能声明而不能一起使用。然而,如果已经赋值了,可以使用std::tie()将std::pair或std::tuple的值进行再赋值。
实现如下:
- auto [a,b,c] = getTuple(); // a,b,c是返回的 tuple的类型和值 ..
复制代码- std::tie(a,b,c) = getTuple(); // a,b,c得到下一个返回的tuple的值
复制代码 这就特别适合用于实现循环调用和处理返回值是pair的情况,比如在循环中进行搜索:
- std::boyer_moore_searcher bmsearch{sub.begin(), sub.end()};
复制代码- for (auto [beg, end] = bmsearch(text.begin(), text.end());
复制代码- std::tie(beg,end) = bmsearch(end, text.end())) {
复制代码 请继续关注本公众号下一篇《C++17特性:结构化绑定(四)自定义API支持结构化绑定》。
长按二维码关注公众号“软件开发谈”
|
|