【C++】競程筆記(實作技巧:Range-based for loop、Structured binding)
程式碼範例參考:NTUCPC Guide,此筆記僅為個人學習用途。
Range-based for loop
這個可以提供更簡潔的遍歷寫法,如下例子:
1 2 3 4
| vector <int> v = {1,2,3,4,5,6,7,8,9}; for (int i : v){ cout << i << endl; }
|
若要對 i 進行修改,需要加上 &,如:int& i。
字串陷阱
1 2 3 4 5 6 7 8 9 10
| #include <bits/stdc++.h>
using namespace std;
int main(){ for (char c : "abc"){ cout << c << "*\n"; } return 0; }
|
輸出:
C / C++ 儲存字串時,是使用字元陣列的形式,他會在最後一個地方加上 \0,表示結束的意思。
所以可看到最後會有一個空格。
要解決此問題僅需要在 “abc” 後面加上一個 s 變成 "abc"s,使這串文字轉換成 C++ 字串型態。
1 2 3 4 5 6 7 8 9 10
| #include <bits/stdc++.h>
using namespace std;
int main(){ for (char c : "abc"s){ cout << c << "*\n"; } return 0; }
|
輸出:
Structured binding
以下是 pair 容器的範例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #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(巨集、宏) 這個小技巧去縮短它的長度。
1 2
| #define f first #define s second
|
完整範例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #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 後的範例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #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);
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 也可用以下型態:
- array
- tuple-like
- 以 struct 或 class 描述的 data members
array
1 2
| 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。
如:
1 2
| tuple<int, double, bool> tup = {1, 0.5, true}; auto [a, b, c] = tup;
|
以 struct 或 class 描述的 data members
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #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; }
|
輸出:
:::danger
vector 不能使用 Structured binding。
:::
所以在讀取 struct 的時候,x y 只會代表 struct 裡面的變數 x y,並不是 vector。