---
# System prepended metadata

title: 【C++】競程筆記（實作技巧：Range-based for loop、Structured binding）
tags: [C++, 競程, 演算法, CP]

---

【C++】競程筆記（實作技巧：Range-based for loop、Structured binding）
===

[TOC]

程式碼範例參考：NTUCPC Guide，此筆記僅為個人學習用途。

Range-based for loop
---

這個可以提供更簡潔的遍歷寫法，如下例子：

```cpp=
vector <int> v = {1,2,3,4,5,6,7,8,9};
for (int i : v){
    cout << i << endl;
}
```

若要對 i 進行修改，需要加上 &，如：`int& i`。

### 字串陷阱
---

```cpp=
#include <bits/stdc++.h>

using namespace std;

int main(){
    for (char c : "abc"){
        cout << c << "*\n";
    }
    return 0;
}
```

輸出：

```
a*
b*
c*
 *
```

C / C++ 儲存字串時，是使用字元陣列的形式，他會在最後一個地方加上 `\0`，表示結束的意思。

所以可看到最後會有一個空格。

要解決此問題僅需要在 "abc" 後面加上一個 s 變成 `"abc"s`，使這串文字轉換成 C++ 字串型態。

```cpp=
#include <bits/stdc++.h>

using namespace std;

int main(){
    for (char c : "abc"s){
        cout << c << "*\n";
    }
    return 0;
}
```

輸出：

```
a*
b*
c*
```

Structured binding
---

以下是 pair 容器的範例：

```cpp=
#include <iostream>
#include <utility>
#include <vector>
#include <string>

using namespace std;

int main() {
    pair<string, int> student1("王奕翔", 0);
    
    pair<string, int> student2 = make_pair("LukeTseng", 100);

    vector<pair<string, int>> studentList;
    
    studentList.push_back(student1);
    studentList.push_back(student2);
    studentList.push_back(make_pair("Bob", 78));

    for (const auto& student : studentList) {
        cout << "學生姓名：" << student.first << "，成績：" << student.second << endl;
    }

    return 0;
}
```

在撰寫 first 跟 second 的時候，會有點麻煩，所以我們可以透過 marco（巨集、宏） 這個小技巧去縮短它的長度。

```cpp=
#define f first
#define s second
```

完整範例如下：

```cpp=
#include <iostream>
#include <utility>
#include <vector>
#include <string>

#define f first
#define s second

using namespace std;

int main() {
    pair<string, int> student1("王奕翔", 0);
    
    pair<string, int> student2 = make_pair("LukeTseng", 100);

    vector<pair<string, int>> studentList;
    
    studentList.push_back(student1);
    studentList.push_back(student2);
    studentList.push_back(make_pair("Bob", 78));

    for (const auto& student : studentList) {
        cout << "學生姓名：" << student.f << "，成績：" << student.s << endl;
    }

    return 0;
}
```

使用這樣的 marco 的缺點：

* 如果我們同時宣告兩個變數 f 和 first，這樣會直接 CE。
* 當 pair 描述的物件是區間、平面上的二維點這種物件時，使用 f 和 s 其實相對不直覺，也許在這時候我們會比較喜歡使用 l, r 或 x, y。

from：NTUCPC Guide

將 f、s 的名稱自定義，會提升程式在閱讀上的可讀性，因此就需要 Structured binding。

以下就是 Structured binding 後的範例：

```cpp=
#include <iostream>
#include <utility>
#include <vector>
#include <string>

using namespace std;

int main() {
    vector<pair<string, int>> studentList;
    studentList.emplace_back("王奕翔", 0);
    studentList.emplace_back("LukeTseng", 100);
    studentList.emplace_back("Bob", 78);

    // 使用 structured binding 取出 pair 的元素
    for (const auto& [name, score] : studentList) {
        cout << "學生姓名：" << name << "，成績：" << score << endl;
    }

    return 0;
}
```

註：emplace_back 是用於 vector 容器的成員函數，於 C++ 11 中被引入的特性，他比原先的 push_back() 更好、更有效率，使用上也與 push_back() 差不多。

兩者差異在於：

* push_back(obj) 需要先建立 obj 物件，再將它複製和移動到 vector 中。
* emplace_back(args...) 不用建立 obj 物件，而是直接在 vector 中構建，少了複製和移動的動作，能提升效率。

在 `[name, score]` 的部分就是用到結構化綁定。

假設 `pair<int, int> a`，則結構化綁定 a 只要寫如下例子即可：

`auto [name, score] = a`

> Structured binding 在 C++17 後才正式成為 C++ 的標準，因此在更低的版本有可能會不適用。不過如果不幸遇到很舊的 C++ 版本，其實可以找時間測看看競賽使用的編譯器認不認得這個功能，有時候即使版本不對，編譯器看得懂的話只會跳警告但還是會幫你編譯完成。
> 
> 儘管實務上不太建議，但競程中如果因為舊版本綁手綁腳就有點太虧了，所以機器測試是很重要的！
> from NTUCPC Guide

### 結構化綁定的實際適用型態
---

除了 pair 也可用以下型態：

1. array
2. tuple-like
3. 以 struct 或 class 描述的 data members

### array

```cpp=
int arr[5] = {1,2,3,4,5};
auto [a, b, c, d, e] = arr;
```

其 `a, b, c, d, e` 分別對應 `arr[0], arr[1], arr[2], arr[3], arr[4]`。

### tuple-like

如同 pair，將多種資料型態打包在一起的組合叫做 tuple-like。

> 不過其實只要大於兩種型別，在 C++ 中就會必須得使用 std::tuple。

如：

```cpp=
tuple<int, double, bool> tup = {1, 0.5, true};
auto [a, b, c] = tup;
```

### 以 struct 或 class 描述的 data members

```cpp=
#include <bits/stdc++.h>

using namespace std;

struct my_struct {
    int x = 1, y = 2;
    vector<int> v = {3, 4, 5};
};

int main(){
	my_struct S;
    auto [x, y, z] = S;
    cout << x << " " << y;
    return 0;
}
```

輸出：

```
1 2
```

:::danger
vector 不能使用 Structured binding。
:::

所以在讀取 struct 的時候，x y 只會代表 struct 裡面的變數 x y，並不是 vector。