Skip to content

Commit 81c07ed

Browse files
author
zhangyuanlong
committed
add articles
Change-Id: I350c41d90b39ece14b29e32bc3fd13e4a3ca708d
1 parent 26510f5 commit 81c07ed

File tree

588 files changed

+42867
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

588 files changed

+42867
-1
lines changed

articles

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
## C++ 17 结构化绑定
2+
3+
stl 的 map 容器很多读者应该都很熟悉,map 容器提供了一个 **insert** 方法,我们用该方法向 map 中插入元素,但是应该很少有人记得 **insert** 方法的返回值是什么类型,让我们来看一下 C++98/03 提供的 **insert** 方法的签名:
4+
5+
```
6+
std::pair<iterator,bool> insert( const value_type& value );
7+
```
8+
9+
这里我们仅关心其返回值,这个返回值是一个 **std::pair** 类型,由于 map 中的元素的 key 不允许重复,所以如果 insert 方法调用成功,T1 是被成功插入到 map 中的元素的迭代器,T2 的类型为 bool,此时其值为 true(表示插入成功);如果 insert 由于 key 重复,T1 是造成 insert 插入失败、已经存在于 map 中的元素的迭代器,此时 T2 的值为 false(表示插入失败)。
10+
11+
在 C++98/03 标准中我们可以使用 **std::pair****first****second** 属性来分别引用 T1 和 T2 的值。如下面的我们熟悉的代码所示:
12+
13+
```
14+
#include <iostream>
15+
#include <string>
16+
#include <map>
17+
18+
int main()
19+
{
20+
std::map<std::string, int> cities;
21+
cities["beijing"] = 0;
22+
cities["shanghai"] = 1;
23+
cities["shenzhen"] = 2;
24+
cities["guangzhou"] = 3;
25+
26+
//for (const auto& [key, value] : m)
27+
//{
28+
// std::cout << key << ": " << value << std::endl;
29+
//}
30+
31+
//这一行在 C++11 之前写法实在太麻烦了,
32+
//std::pair<std::map<std::string, int>::iterator, int> insertResult = cities.insert(std::pair<std::string, int>("shanghai", 2));
33+
//C++ 11中我们写成:
34+
auto insertResult = cities.insert(std::pair<std::string, int>("shanghai", 2));
35+
36+
std::cout << "Is insertion successful ? " << (insertResult.second ? "true" : "false")
37+
<< ", element key: " << insertResult.first->first << ", value: " << insertResult.first->second << std::endl;
38+
39+
return 0;
40+
}
41+
```
42+
43+
代码 **19** 行实在太啰嗦了,我们使用 auto 关键字让编译器自动推导类型。
44+
45+
**std::pair** 一般只能表示两个元素,C++11 标准中引入了 **std::tuple** 类型,有了这个类型,我们就可以放任意个元素了,原来需要定义成结构体的 POD 对象我们可以直接使用 **std::tuple** 表示,例如下面表示用户信息的结构体:
46+
47+
```
48+
struct UserInfo
49+
{
50+
std::string username;
51+
std::string password;
52+
int gender;
53+
int age;
54+
std::string address;
55+
};
56+
57+
int main()
58+
{
59+
UserInfo userInfo = { "Tom", "123456", 0, 25, "Pudong Street" };
60+
std::string username = userInfo.username;
61+
std::string password = userInfo.password;
62+
int gender = userInfo.gender;
63+
int age = userInfo.age;
64+
std::string address = userInfo.address;
65+
66+
return 0;
67+
}
68+
```
69+
70+
我们不再需要定义 struct UserInfo 这样的对象,可以直接使用 **std::tuple** 表示:
71+
72+
```
73+
int main()
74+
{
75+
std::tuple<std::string, std::string, int, int, std::string> userInfo("Tom", "123456", 0, 25, "Pudong Street");
76+
77+
std::string username = std::get<0>(userInfo);
78+
std::string password = std::get<1>(userInfo);
79+
int gender = std::get<2>(userInfo);
80+
int age = std::get<3>(userInfo);
81+
std::string address = std::get<4>(userInfo);
82+
83+
return 0;
84+
}
85+
```
86+
87+
**std::tuple** 中获取对应位置的元素,我们使用 **std::get** ,其中 N 是元素的序号(从 0 开始)。
88+
89+
与定义结构体相比,通过 **std::pair****first****second** 还是 **std::tuple****std::get** 方法来获取元素子属性,这些代码都是非常难以维护的,其根本原因是 **first****second** 这样的命名不能做到见名知意。
90+
91+
C++17 引入的**结构化绑定**(Structured Binding )将我们从这类代码中解放出来。**结构化绑定**使用语法如下:
92+
93+
```
94+
auto [a, b, c, ...] = expression;
95+
auto [a, b, c, ...] { expression };
96+
auto [a, b, c, ...] ( expression );
97+
```
98+
99+
右边的 **expression** 可以是一个函数调用、花括号表达式或者支持结构化绑定的某个类型的变量。例如:
100+
101+
```
102+
//形式1
103+
auto [iterator, inserted] = someMap.insert(...);
104+
//形式2
105+
double myArray[3] = { 1.0, 2.0, 3.0 };
106+
auto [a, b, c] = myArray;
107+
//形式3
108+
struct Point
109+
{
110+
double x;
111+
double y;
112+
};
113+
Point myPoint(10.0, 20.0);
114+
auto [myX, myY] = myPoint;
115+
```
116+
117+
这样,我们可以给用于绑定到目标的变量名(语法中的 **a****b****c**)起一个有意义的名字。
118+
119+
需要注意的是,绑定名称 **a****b****c** 是绑定目标的一份拷贝,当绑定类型不是基础数据类型时,如果你的本意不是想要得到绑定目标的副本,为了避免拷贝带来的不必要开销,建议使用引用,如果不需要修改绑定目标建议使用 const 引用。示例如下:
120+
121+
```
122+
double myArray[3] = { 1.0, 2.0, 3.0 };
123+
auto& [a, b, c] = myArray;
124+
//形式3
125+
struct Point
126+
{
127+
double x;
128+
double y;
129+
};
130+
Point myPoint(10.0, 20.0);
131+
const auto& [myX, myY] = myPoint;
132+
```
133+
134+
**结构化绑定**(Structured Binding )是 C++17 引入的一个非常好用的语法特性。有了这种语法,在遍历像 map 这样的容器时,我们可以使用更简洁和清晰的代码去遍历这些容器了:
135+
136+
```
137+
std::map<std::string, int> cities;
138+
cities["beijing"] = 0;
139+
cities["shanghai"] = 1;
140+
cities["shenzhen"] = 2;
141+
cities["guangzhou"] = 3;
142+
143+
for (const auto& [cityName, cityNumber] : cities)
144+
{
145+
std::cout << cityName << ": " << cityNumber << std::endl;
146+
}
147+
```
148+
149+
上述代码中 **cityName****cityNumber** 可以更好地反映出这个 map 容器的元素内容。
150+
151+
我们再来看一个例子,某 WebSocket 网络库(https://github.com/uNetworking/uWebSockets)中有如下代码:
152+
153+
```
154+
std::pair<int, bool> uncork(const char *src = nullptr, int length = 0, bool optionally = false) {
155+
LoopData *loopData = getLoopData();
156+
157+
if (loopData->corkedSocket == this) {
158+
loopData->corkedSocket = nullptr;
159+
160+
if (loopData->corkOffset) {
161+
/* Corked data is already accounted for via its write call */
162+
auto [written, failed] = write(loopData->corkBuffer, loopData->corkOffset, false, length);
163+
loopData->corkOffset = 0;
164+
165+
if (failed) {
166+
/* We do not need to care for buffering here, write does that */
167+
return {0, true};
168+
}
169+
}
170+
171+
/* We should only return with new writes, not things written to cork already */
172+
return write(src, length, optionally, 0);
173+
} else {
174+
/* We are not even corked! */
175+
return {0, false};
176+
}
177+
}
178+
```
179+
180+
代码的第 **9****write** 函数返回类型是 **std::pair**,被绑定到 **[written, failed]** 这两个变量中去。前者在写入成功的情况下表示实际写入的字节数,后者表示是否写入成功。
181+
182+
```
183+
std::pair<int, bool> write(const char *src, int length, bool optionally = false, int nextLength = 0) {
184+
//具体实现省略...
185+
}
186+
```
187+
188+
**结构化绑定的限制**
189+
190+
结构化绑定不能使用 **constexpr** 修饰或被申明为 static,例如:
191+
192+
```
193+
//正常编译
194+
auto [first, second] = std::pair<int, int>(1, 2);
195+
//无法编译通过
196+
//constexpr auto [first, second] = std::pair<int, int>(1, 2);
197+
//无法编译通过
198+
//static auto [first, second] = std::pair<int, int>(1, 2);
199+
```
200+
201+
注意:有些编译器也不支持在 lamda 表达式捕获列表中使用结构化绑定语法。

0 commit comments

Comments
 (0)