Cpp 结构化绑定 学习笔记
结构化绑定是C++17引入的一个重要的语法糖,可以让代码写得简洁不少,值得好好整理一下语法。
概述
结构化绑定(structured binding)是 C++17 标准引入的一项特性,
允许开发者在解包元组
(std::tuple
)、std::pair
、数组或自定义结构体等数据结构时,
将其中各个元素直接绑定到多个变量上,使得代码更加简洁易读。
基本用法
最基本的语法形如 1
auto [a0,a1,a2] = data;
这一行语句可以对data
进行结构化绑定,要求:
- 左侧必须使用
auto
开头(其实还可以加上const
,&
等修饰,这里暂不讨论,见下文) - 左侧具体需要的变量个数由右侧数据决定。
a0
等是合法的C++标识符,并且不能是已经定义过的标识符
结构化绑定过程中会自动用a0
等作为标识符定义变量,将其依次对应data
中的元素值。
假设data
是一个具有三个元素的自定义结构体 1
2
3
4
5struct Person {
std::string name;
int age;
double height;
};
那么结构化绑定的语法基本等效于 1
2
3
4
5
6// auto [a0,a1,a2] = data;
auto tmp_data = data;
auto &a0 = tmp_data.name;
auto &a1 = tmp_data.age;
auto &a2 = tmp_data.height;
这也表明结构化绑定中发生了一次拷贝,对元素a0
的修改不会影响到原本的data
,反之也一样。
下面是对几类数据结构进行结构化绑定的基本示例
1 |
|
进阶用法
前面auto
开头的结构化绑定中,隐含了一次对象的复制,
我们还可以加上const
和&
修饰得到以下几类用法:(这里以自定义结构体为例,其它数据对象是一样的)
1 | struct Person { |
通过cppinsights去掉语法糖,可以得到编译器的具体实现可能为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19Person __data16 = Person(data);
auto &a0 = __data16.name;
int &a1 = __data16.age;
double &a2 = __data16.height;
Person &__data17 = data;
auto &b0 = __data17.name;
int &b1 = __data17.age;
double &b2 = __data17.height;
const Person __data18 = Person(data);
const auto &c0 = __data18.name;
const int &c1 = __data18.age;
const double &c2 = __data18.height;
const Person &__data19 = data;
const auto &d0 = __data19.name;
const int &d1 = __data19.age;
const double &d2 = __data19.height;
这里__data16
等只是编译器生成的临时标识符,无法直接获取。
对比可以发现const
和&
实际上是作用在绑定之前的临时变量之上:
auto
:生成tmp_data
作为data
的拷贝auto &
:生成tmp_data
作为data
的引用const
:生成tmp_data
作为data
的常量拷贝const auto &
:生成tmp_data
作为data
的常量引用
注意:并不是每一种自定义类(结构体)都可以被结构化绑定,必须满足如下条件:
- 所有的非静态数据成员在当前语境中均可访问,这并不要求全部成员都是public。
- 所有的非静态数据成员都是它自己,或者同一个基类的直接成员。
第一个要求例如 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class Demo {
int c1, c2;
public:
Demo() = default;
void foo(Demo c){
auto [a, b] = c; // C的成员函数内,可以访问它的私有成员
}
};
int main() {
Demo c{};
auto [a, b] = c; // 外部函数无法访问C的私有成员,编译报错
return 0;
}
第二个要求例如 1
2
3
4
5
6
7
8
9
10
11
12
13struct A {
int a1, a2;
};
struct B : A {};
struct C : A { int c; };
int main() {
auto [x1,x2] = B{};
auto [y1,y2,y3] = C{}; // 编译报错
return 0;
}
此时对于自定义类型B
可以进行结构化绑定,但是自定义类型C
不行。