第 18.6 節

結構化綁定

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

本節解決什麼問題

當函數需要返回多個值(如成功標誌 + 數據),或者遍歷 map 時需要同時拿到 key 和 value,在 C++17 之前需要藉助 std::pair / std::tuple + std::get<>().first / .second,代碼冗長且不易讀。

結構化綁定讓你可以一行語句把複合類型(pair、tuple、結構體)分解成獨立的變量,代碼清晰很多。

這個特性是什麼

結構化綁定(Structured Bindings)是 C++17 引入的語法,允許你將一個結構體、pair、tuple 或數組的元素"綁定"到獨立的變量名上。

C++ 標準版本

C++17

需要的頭文件

不需要額外頭文件。結構化綁定是語言特性。如果用於 tuple 需要 <tuple>,用於 pair 需要 <utility>

基本語法

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

常用用法

用法說明
auto [x, y] = pair解包 pair
auto [a, b, c] = std::tuple<...>(...)解包 tuple
auto [x, y, z] = struct_obj解包結構體(按成員順序)
for (auto& [k, v] : map)遍歷 map 時解包 key/value
auto [it, ok] = map.insert(...)解包 insert 返回的 pair<iterator, bool>

示例代碼

示例 1:函數返回 pair,用結構化綁定解包

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

運行結果

10 / 2 = 5
10 / 0: division failed

示例 2:在示例 1 基礎上,解包 tuple 和數組

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

運行結果

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

示例 3:在示例 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;
}

運行結果

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

示例 4:在 map 遍歷和 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;
}

運行結果

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

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

運行結果

見上方每個示例的"運行結果"。

示例中的關鍵語法解釋

示例講了什麼新出現的語法爲什麼這樣寫注意事項
示例 1解包 pairauto [result, ok] = pair函數返回 pair<值, 狀態> 是常見模式變量名可以任意起,按順序對應
示例 2解包 tuple 和數組auto [a,b,c] = tupleauto [a,b,c] = arraytuple 多值返回比 pair 更靈活變量個數必須和 tuple/數組成員數一致
示例 3解包結構體auto [x,y,z] = struct_obj按結構體成員聲明順序綁定所有成員必須是 public,且順序確定
示例 4map 遍歷和 insertauto [it, ok] = m.insert(...)[name, score]遍歷 map 最清晰的寫法insert 返回 pair<iterator, bool>

auto [a, b]auto& [a, b] 的區別

結構化綁定默認會拷貝。遍歷大對象或想修改原數據時,要寫引用。

示例 5:修改 map 的 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;
}

運行結果

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

注意:map 的 key 仍然不能改。即使用 auto& [name, score]name 也是隻讀的,因爲 map 必須保持 key 的排序規則不被破壞。

常見錯誤

錯誤 1:變量個數不匹配

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

正確做法:變量個數必須等於複合類型的元素個數。

錯誤 2:解包非公有成員的結構體

struct A { private: int x; public: int y; };
A obj;
auto [x, y] = obj;  // ❌ 编译错误!结构体有私有成员时不能解包

正確做法:所有成員都必須是 public。

錯誤 3:用 auto 而不是 auto& 導致修改無效

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

正確做法:要修改原值用 auto& [k, v] = ...

使用建議

  1. 函數返回多值時優先用 std::tuple/std::pair + 結構化綁定,而不是輸出參數。
  2. 遍歷 map 永遠用 for (const auto& [key, val] : map),代碼可讀性最高。
  3. 解包 insert 返回值auto [it, inserted] = map.insert(...).second 更清晰。
  4. auto& 用於需要修改的場景const auto& 用於只讀場景。
  5. 遍歷 map 時優先 const auto& [k, v]:避免拷貝,也表達只讀意圖。

小結

  • 結構化綁定可以把 pair / tuple / 結構體 / 數組分解成獨立變量。
  • 語法:auto [var1, var2, ...] = expression
  • 遍歷 map 時 [key, value] 是最佳實踐(C++17)。
  • insert 返回值用 [iterator, bool] 解包非常清晰。
音乐页