第 18.6 節

structured binding

0瀏覽次數0訪問次數--跳出率--平均停留

What problem does this section solve?

When a function needs to return multiple values (like a success flag plus data), or when you need to access both the key and value while iterating through a map, prior to C++17 you had to use workarounds like std::pair / std::tuple with std::get<>(), or .first / .second—making the code verbose and harder to read.

Structured bindings allow you to decompose composite types (pair, tuple, structs) into individual variables in one line of code, making the code much clearer.

What is this feature?

Structured Bindings, introduced in C++17, allow you to "bind" elements from a struct, pair, tuple, or array to individual variable names.

C++ standard version

C++17

Required header files

No additional header files are needed. Structured binding is a language feature. For use with tuples, you need <tuple>, and for use with pairs, you need <utility>.

Basic Syntax

auto [变量1, 变量2, ...] = 表达式;        // 值拷贝绑定
auto& [变量1, 变量2, ...] = 表达式;       // 引用绑定(可修改原值)
const auto& [变量1, 变量2, ...] = 表达式;  // 只读引用绑定

Common Usage

UsageExplanation
auto [x, y] = pairUnpack pair
auto [a, b, c] = std::tuple<...>(...)Unpack tuples
auto [x, y, z] = struct_objUnpack struct (in member order)
for (auto& [k, v] : map)Traversing a map while unpacking key/value pairs
auto [it, ok] = map.insert(...)Unpack the pair<iterator, bool> returned by insert.

Example code

Example 1: A function returns a pair, unpack using 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 结构化绑定:一行解包
    auto [result, ok] = divide(10, 2);
    if (ok)
    {
        std::cout << "10 / 2 = " << result << "\n";
    }

    auto [result2, ok2] = divide(10, 0);
    if (!ok2)
    {
        std::cout << "10 / 0: division failed\n";
    }

    return 0;
}

Results

10 / 2 = 5
10 / 0: division failed

Example 2: Unpacking tuple and array

#include <iostream>
#include <tuple>

// 返回多个值
std::tuple<std::string, int, double> get_student_info()
{
    return {"Alice", 20, 3.8};  // 姓名、年龄、GPA
}

int main()
{
    // 解包 tuple
    auto [name, age, gpa] = get_student_info();
    std::cout << "Name: " << name << "\n";
    std::cout << "Age: " << age << "\n";
    std::cout << "GPA: " << gpa << "\n";

    // 解包数组
    int arr[] = {10, 20, 30};
    auto [a, b, c] = arr;
    std::cout << "arr: " << a << ", " << b << ", " << c << "\n";

    return 0;
}

Results

Name: Alice
Age: 20
GPA: 3.8
arr: 10, 20, 30

Example 3: Unpacking the struct building on Example 2

#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};

    // 解包结构体
    auto [x, y, z] = midpoint(p1, p2);
    std::cout << "midpoint: (" << x << ", " << y << ", " << z << ")\n";

    // 解包返回的临时 Point 对象
    auto [a, b, c] = Point{10.0, 20.0, 30.0};
    std::cout << "point: (" << a << ", " << b << ", " << c << ")\n";

    return 0;
}

Results

midpoint: (3, 4, 5)
point: (10, 20, 30)

Example 4: Using Structured Bindings in Map Traversal and Insert

#include <iostream>
#include <map>
#include <string>

int main()
{
    std::map<std::string, int> scores;

    // insert 返回 pair<iterator, bool>
    auto [it1, inserted1] = scores.insert({"Alice", 85});
    if (inserted1)
    {
        std::cout << "Alice inserted, score: " << it1->second << "\n";
    }

    // 再次插入同名(会失败)
    auto [it2, inserted2] = scores.insert({"Alice", 100});
    if (!inserted2)
    {
        std::cout << "Alice already exists, score: " << it2->second << "\n";
    }

    // 再插入几个
    scores.insert({"Bob", 92});
    scores.insert({"Charlie", 78});

    // 遍历 map,结构化绑定解包 key/value
    std::cout << "\nAll scores:\n";
    for (const auto& [name, score] : scores)
    {
        std::cout << "  " << name << ": " << score << "\n";
    }

    return 0;
}

Results

Alice inserted, score: 85
Alice already exists, score: 85

All scores:
  Alice: 85
  Bob: 92
  Charlie: 78

runtime results

See the "running results" for each example above.

Key syntax explanation in the example

|Here is the translation of the provided Simplified Chinese Markdown fragment into natural American English, following all specified rules.


ExampleDiscusses whatNewly emerged syntaxWhy write it this wayPrecautions
Example 1Unpack pairauto [result, ok] = pairA function returning a pair of value and status is a common pattern.Variable names can be chosen freely, corresponding in order.
Example 2Unpacking tuples and arrays.auto [a,b,c] = tupleauto [a,b,c] = arrayReturning multiple values with a tuple is more flexible than using a pair.The number of variables must match the number of members in the tuple/array.
Example 3Unpacking a structauto [x,y,z] = struct_objBind according to the declaration order of struct members.All members must be public, and the order is fixed.
Example 4map traversal and insertauto [it, ok] = m.insert(...)[name, score]The clearest way to iterate over a map is by using the Object.entries() method, which converts the map into an array of key-value pairs that can be easily looped through.
// Example map
const map = new Map([
    ['name', 'Alice'],
    ['age', 25],
    ['job', 'Engineer']
]);

// Iterate with clear key-value pairs
for (const [key, value] of map.entries()) {
    console.log(`${key}: ${value}`);
}

This approach is explicit, readable, and works across modern JavaScript environments.|insert returns pair<iterator, bool>|

The difference between auto [a, b] and auto& [a, b]

Structured bindings create copies by default. When traversing large objects or needing to modify the original data, you should use references.

Example 5: Must use a reference binding when modifying a map's value

#include <iostream>
#include <map>
#include <string>

int main()
{
    std::map<std::string, int> scores = {
        {"Alice", 85},
        {"Bob", 92}
    };

    for (auto [name, score] : scores)
    {
        score += 10;  // 修改的是拷贝
    }

    std::cout << "after auto copy:\n";
    for (const auto& [name, score] : scores)
    {
        std::cout << name << ": " << score << "\n";
    }

    for (auto& [name, score] : scores)
    {
        score += 10;  // 修改原 map 的 value
    }

    std::cout << "after auto& reference:\n";
    for (const auto& [name, score] : scores)
    {
        std::cout << name << ": " << score << "\n";
    }

    return 0;
}

Results

after auto copy:
Alice: 85
Bob: 92
after auto& reference:
Alice: 95
Bob: 102

Note: The keys map still cannot be altered. Even using auto& [name, score] or name remains read-only, as the map's key sorting rules must not be disrupted.

Common Errors

Error 1: Variable count mismatch

auto [x, y] = std::make_tuple(1, 2, 3);  // ❌ left has 2, right has 3

Correct approach: The number of variables must match 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;
auto [x, y] = obj;  // ❌ 编译错误!结构体有私有成员时不能解包

The correct approach: All members must be public.

Error 3: Using auto instead of auto& causes changes to be ineffective

auto [k, v] = *map.begin();  // 拷贝了!
v = 100;  // 只修改了拷贝

The correct approach is to use auto& [k, v] = ... to modify the original value.

使用建议

  • 明确目标:在开始前确定您的具体需求,以便选择最合适的工具或教程。
  • 充分利用资源:参考官方文档、教程和博客,这些资料能帮助您快速上手并解决问题。
  • 实践应用:通过动手操作项目或编写代码来巩固学习成果,提升实际操作能力。
  • 问题解决:遇到困难时,查阅参考资料或寻求社区支持,逐步培养独立解决问题的能力。
  • 分享经验:完成项目后,可以撰写文章或博客分享心得,帮助其他学习者。

如果需要针对特定领域(如单片机、机器人或环境搭建)的进一步建议,请提供更多信息,我将为您细化内容。

  1. When a function returns multiple values, prefer std::tuple/std::pair + structured bindings over output parameters.
  2. Always use for (const auto& [key, val] : map) when traversing a map, for the highest code readability.
  3. Unpacking the return value from insert: auto [it, inserted] = map.insert(...) is clearer than .second.
  4. auto& is for scenarios requiring modifications, const auto& is for read-only scenarios.
  5. Prefer const auto& [k, v] when traversing maps: avoid copies and also convey read-only intent.

Summary

  • Structured bindings can decompose a pair, tuple, struct, or array into individual variables.
  • Syntax: auto [var1, var2, ...] = expression.
  • When iterating through a map, [key, value] is best practice (C++17).
  • Using the [iterator, bool] to unpack the return value is quite clear.
音乐页