Wiki
Structured binding
Existing English translations were reused. 3 current source blocks remain in Simplified Chinese, without triggering paid translation.
1.What problem does this section solve?
When a function needs to return multiple values (such as a success flag + data), or when iterating over a map and needing to obtain both the key and value at the same time, before C++17 this required the use of std::pair / std::tuple + std::get<>() or .first / .second; the code was verbose and hard to read.
Structured bindings let youdestructure a composite type (pair, tuple, struct) into separate variables in a single statement—much clearer code.
2.What is this feature?
Structured Bindings are a syntax introduced in C++17 that allow you to "bind" the elements of a struct, pair, tuple, or array to independent variable names.
3.C++ Standard Edition
C++17. To compile the complete example in this section, a standard of C++17 or higher must be enabled, for example:
g++ example.cpp -std=c++17
4.Required header files
不需要额外头文件。结构化绑定是语言特性。如果用于 tuple 需要 <tuple>,用于 pair 需要 <utility>。
5.From the syntax before C++17 to structured bindings
C++Before 17, there was no unified "unpacking" syntax. For different composite types, their respective access methods needed to be used:
| Types | Access methods before C++17 | C++17 structured bindings |
|---|---|---|
std::pair |
.first、.second |
auto [first, second] = pair; |
std::tuple |
std::get<0>()、std::get<1>() etc. |
auto [a, b, ...] = tuple; |
| Structs | .成员名 |
auto [a, b, ...] = object; |
| arrays | [0]、[1] etc. subscripts |
auto [a, b, ...] = array; |
Taking std::pair as an example, before C++17 it is necessary to access first and second respectively:
std::pair<int, bool> result = {5, true};
// C++17 之前:通过 pair 固定的成员名 first 和 second 取出两个值。
int value = result.first;
bool ok = result.second;
C++Starting from 17, structured bindings can be used to extract two values at once and name them according to their meaning:
std::pair<int, bool> result = {5, true};
// C++17:value 对应 result.first,ok 对应 result.second。
auto [value, ok] = result;
Structured bindings do not change the pair、tuple、struct, or array itself; they merely provide a unified and concise syntax for extracting values. The variables in square brackets correspond one-to-one in the original order of the elements or members.
6.Basic syntax
// 以下三种写法都是 C++17 开始支持的结构化绑定。
auto [变量1, 变量2, ...] = 表达式; // 值绑定:通常会得到各元素的副本。
auto& [变量1, 变量2, ...] = 表达式; // 引用绑定:可以通过变量修改原对象。
const auto& [变量1, 变量2, ...] = 表达式; // 只读引用绑定:不拷贝,也不允许修改。
7.Common Usage
The syntax in the following table requires enabling C++17 or a later version:
| Usage | Description |
|---|---|
auto [x, y] = pair |
Unpack pair |
auto [a, b, c] = std::tuple<...>(...) |
Unpack tuple |
auto [x, y, z] = struct_obj |
Unpack struct (in member order) |
for (auto& [k, v] : map) |
Unpack when iterating over a map key/value |
auto [it, ok] = map.insert(...) |
Unpack the pair<iterator, bool> returned by insert |
8.Example code
8.1.Example 1: Function returning a pair—from member access before C++17 to structured bindings
#include <iostream>
#include <utility> // for std::pair
// 返回两个值的函数
std::pair<int, bool> divide(int a, int b)
{
if (b == 0)
{
return {0, false}; // 除数为 0,返回失败
}
return {a / b, true}; // 返回结果和成功标志
}
int main()
{
// C++17 之前:先接收完整的 pair 对象。
const std::pair<int, bool> old_result = divide(10, 2);
// pair 的 first 是第一个值,这里代表除法结果。
const int old_value = old_result.first;
// pair 的 second 是第二个值,这里代表操作是否成功。
const bool old_ok = old_result.second;
if (old_ok)
{
std::cout << "before C++17: 10 / 2 = " << old_value << "\n";
}
// C++17:结构化绑定把返回的 pair 按顺序拆成 value 和 ok。
// value 对应 pair.first,ok 对应 pair.second。
const auto [value, ok] = divide(10, 2);
if (ok)
{
std::cout << "C++17: 10 / 2 = " << value << "\n";
}
// C++17:同样的方法也可以接收失败结果。
const auto [failed_value, failed_ok] = divide(10, 0);
// failed_ok 为 false,说明 failed_value 不是有效的除法结果。
if (!failed_ok)
{
std::cout << "10 / 0: division failed\n";
}
return 0;
}
Running result:
before C++17: 10 / 2 = 5
C++17: 10 / 2 = 5
10 / 0: division failed
8.2.Example 2: tuple and array—from element-by-element access to C++17 structured bindings
#include <iostream>
#include <string>
#include <tuple>
// 返回多个值
std::tuple<std::string, int, double> get_student_info()
{
return {"Alice", 20, 3.8}; // 姓名、年龄、GPA
}
int main()
{
// C++17 之前:先保存完整的 tuple。
const auto old_info = get_student_info();
// std::get<下标>() 按位置访问 tuple,编号从 0 开始。
const std::string& old_name = std::get<0>(old_info);
const int old_age = std::get<1>(old_info);
const double old_gpa = std::get<2>(old_info);
std::cout << "before C++17 tuple: "
<< old_name << ", " << old_age << ", " << old_gpa << "\n";
// C++17:结构化绑定按 tuple 中元素的顺序声明三个变量。
// const auto& 表示只读引用,可以避免复制 tuple 中的 string。
const auto& [name, age, gpa] = old_info;
std::cout << "C++17 tuple: "
<< name << ", " << age << ", " << gpa << "\n";
int numbers[] = {10, 20, 30};
// C++17 之前:使用下标逐个访问数组元素。
std::cout << "before C++17 array: "
<< numbers[0] << ", " << numbers[1] << ", " << numbers[2] << "\n";
// C++17:数组有三个元素,所以左侧也必须声明三个名字。
// 这里没有写 &,因此 a、b、c 是三个元素的副本。
const auto [a, b, c] = numbers;
std::cout << "C++17 array: " << a << ", " << b << ", " << c << "\n";
return 0;
}
Running result:
before C++17 tuple: Alice, 20, 3.8
C++17 tuple: Alice, 20, 3.8
before C++17 array: 10, 20, 30
C++17 array: 10, 20, 30
8.3.Example 3: Structs — From member access to C++17 structured bindings
#include <iostream>
#include <string>
struct Point
{
double x;
double y;
double z;
};
Point midpoint(const Point& p1, const Point& p2)
{
return {
(p1.x + p2.x) / 2.0,
(p1.y + p2.y) / 2.0,
(p1.z + p2.z) / 2.0
};
}
int main()
{
Point p1{1.0, 2.0, 3.0};
Point p2{5.0, 6.0, 7.0};
// C++17 之前:先接收完整的结构体,再通过成员名访问数据。
const Point old_middle = midpoint(p1, p2);
std::cout << "before C++17 midpoint: ("
<< old_middle.x << ", "
<< old_middle.y << ", "
<< old_middle.z << ")\n";
// C++17:结构化绑定按成员的声明顺序解包结构体。
// x 对应 Point::x,y 对应 Point::y,z 对应 Point::z。
const auto [x, y, z] = midpoint(p1, p2);
std::cout << "C++17 midpoint: (" << x << ", " << y << ", " << z << ")\n";
// C++17:也可以直接解包临时创建的结构体对象。
const auto [a, b, c] = Point{10.0, 20.0, 30.0};
std::cout << "C++17 temporary point: ("
<< a << ", " << b << ", " << c << ")\n";
return 0;
}
Running result:
before C++17 midpoint: (3, 4, 5)
C++17 midpoint: (3, 4, 5)
C++17 temporary point: (10, 20, 30)
8.4.Example 4: map—Compare the insertion results and traversal syntax before and after C++17
#include <iostream>
#include <map>
#include <string>
int main()
{
std::map<std::string, int> scores;
// map::insert 返回一个 pair<iterator, bool>:
// first 是指向相应元素的迭代器,second 表示是否插入成功。
// C++17 之前:先接收完整的 pair,再分别读取 first 和 second。
const auto old_insert_result = scores.insert({"Alice", 85});
const auto old_it = old_insert_result.first;
const bool old_inserted = old_insert_result.second;
if (old_inserted)
{
// 迭代器的 second 是 map 元素的 value,这里是 Alice 的分数。
std::cout << "before C++17 insert: Alice inserted, score: "
<< old_it->second << "\n";
}
// C++17:直接把 insert 返回的 pair 解包为迭代器和成功标志。
// 由于 Alice 已经存在,这次重复插入会失败。
const auto [it, inserted] = scores.insert({"Alice", 100});
if (!inserted)
{
std::cout << "C++17 insert: Alice already exists, score: "
<< it->second << "\n";
}
// 添加另外两个元素,供下面演示遍历使用。
scores.insert({"Bob", 92});
scores.insert({"Charlie", 78});
// C++17 之前:范围 for 每次取得一个 pair。
// pair.first 是 map 的 key,pair.second 是 map 的 value。
std::cout << "\nbefore C++17 traversal:\n";
for (const auto& pair : scores)
{
std::cout << " " << pair.first << ": " << pair.second << "\n";
}
// C++17:在范围 for 中使用结构化绑定,直接命名 key 和 value。
// const auto& 表示只读引用,既不复制元素,也不会修改 map。
std::cout << "C++17 traversal:\n";
for (const auto& [name, score] : scores)
{
std::cout << " " << name << ": " << score << "\n";
}
return 0;
}
Running result:
before C++17 insert: Alice inserted, score: 85
C++17 insert: Alice already exists, score: 85
before C++17 traversal:
Alice: 85
Bob: 92
Charlie: 78
C++17 traversal:
Alice: 85
Bob: 92
Charlie: 78
9.Operation result
See the "run results" for each example above.
10.Key syntax explanation in the example
| Example | What was explained | Newly introduced syntax | Why it is written this way | Notes |
|---|---|---|---|---|
| Example 1 | From pair.first/second changed to destructuring the pair |
auto [value, ok] = pair |
C++17 can declare two meaningful variable names at once | Variable names can be customized, but the corresponding order cannot be changed |
| Example 2 | From std::get<N>() and array indexing changed to unified destructuring |
auto [a,b,c] = tuple、auto [a,b,c] = array |
C++17 uses similar syntax for tuples and arrays | The number of variables must match the number of elements in the tuple or array |
| Example 3 | From .成员名 changed to destructuring a struct |
auto [x,y,z] = struct_obj |
C++17 binds in the order in which struct members are declared | The bindable members must satisfy access conditions, and the order is determined by the struct definition |
| Example 4 | Compare the map syntax before and after C++17 | auto [it, ok] = m.insert(...)、[name, score] |
Return values and map elements can both be extracted and named directly | insert returns pair<iterator, bool> |
11.auto [a, b] and auto& [a, b] difference
The following discussion of auto [a, b] and auto& [a, b] are all C++17 structured binding syntax. When structured bindings do not write &, a copy is usually produced; when iterating over large objects or wanting to modify the original data, you need to consider using references.
C++Before 17, there were no structured bindings. When modifying a map's value, one typically obtained a reference to the entire pair and then modified its second:
// C++17 之前:pair.second 是 map 元素的 value。
for (auto& pair : scores)
{
pair.second += 10;
}
11.1.Example 5: C++17 In structured bindings, modifying a map's value requires using a reference
#include <iostream>
#include <map>
#include <string>
int main()
{
std::map<std::string, int> scores = {
{"Alice", 85},
{"Bob", 92}
};
// C++17:没有写 &,name 和 score 不引用 map 中的原元素。
// 每轮循环得到的是当前 key 和 value 的副本。
for (auto [name, score] : scores)
{
// 这里只修改局部变量 score,scores 中的原分数不会变化。
score += 10;
}
std::cout << "after auto copy:\n";
// C++17:const auto& 不复制 map 元素,适合只读输出。
for (const auto& [name, score] : scores)
{
std::cout << name << ": " << score << "\n";
}
// C++17:auto& 让 name 和 score 引用 map 中的原元素。
for (auto& [name, score] : scores)
{
// score 是 value 的引用,因此这次会修改原 map 中的分数。
score += 10;
}
std::cout << "after auto& reference:\n";
// 修改完成后再次只读遍历,确认新分数已经保存在 map 中。
for (const auto& [name, score] : scores)
{
std::cout << name << ": " << score << "\n";
}
return 0;
}
Running result:
after auto copy:
Alice: 85
Bob: 92
after auto& reference:
Alice: 95
Bob: 102
注意:map 的 key 仍然不能改。即使使用 C++17 的 auto& [name, score],name 也是只读的,因为 map 必须保证 key 不变,才能维持内部的排序规则。
12.Common Errors
Error 1: Mismatched number of variables
// C++17:右侧 tuple 有三个元素,左侧却只有两个变量,无法一一对应。
auto [x, y] = std::make_tuple(1, 2, 3); // ❌ 编译错误
Correct approach: the number of variables must equal the number of elements in the composite type.
Error 2: Unpacking a struct with non-public members
struct A { private: int x; public: int y; };
A obj;
// C++17:此处不能访问私有成员 x,因此不能这样进行结构化绑定。
auto [x, y] = obj; // ❌ 编译错误
正确做法:在使用结构化绑定的位置,所有被绑定的成员都必须可以访问。对初学阶段常见的普通结构体来说,最直观的做法是让这些成员保持 public。
Mistake 3: Using auto instead of auto& results in the modification having no effect
// C++17:没有写 &,k 和 v 是从 map 元素得到的副本。
auto [k, v] = *map.begin();
// 这里只修改局部变量 v,不会修改 map 中的 value。
v = 100;
Correct approach: to modify the original value, use auto& [k, v] = ....
13.Usage Recommendations
- When a function returns multiple values, prefer
std::tuple/std::pair+ structured bindings rather than output parameters. - C++17 When performing read-only traversal of a map, prefer
const auto& [key, value]to avoid copies and to clearly express the meaning of the key and value. - Unpacking the return value of insert: C++17's
auto [it, inserted] = map.insert(...)is more intuitive than continuing to use.first、.second. - Use
auto&when the original data needs to be modified; useconst auto&when it is read-only and you do not want to copy. - When maintaining projects from before C++17, continue to use the original access methods: use
.first/.secondfor pair, and usestd::get<N>()for tuple.
14.Summary
- C++17 Before, it was necessary to access values separately via
.first/.second、std::get<N>()、member names, or array subscripts. - C++17 Structured bindings can decompose a pair / tuple / struct / array into independent variables.
- C++17 The basic syntax is
auto [var1, var2, ...] = expression. - When iterating over a map,
[key, value]is the best practice (C++17). - Unpacking the insert return value with
[iterator, bool]is very clear.