Skip to content

Wiki

Structured binding

About 12 min read

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

  1. When a function returns multiple values, prefer std::tuple/std::pair + structured bindings rather than output parameters.
  2. 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.
  3. Unpacking the return value of insert: C++17's auto [it, inserted] = map.insert(...) is more intuitive than continuing to use .first、.second.
  4. Use auto& when the original data needs to be modified; use const auto& when it is read-only and you do not want to copy.
  5. When maintaining projects from before C++17, continue to use the original access methods: use .first/.second for pair, and use std::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.