Tuple

普通

介紹

在上一章我們學會了 pair ,但你有沒有曾經想要放超過兩項資料呢?其中一個作法就是套兩層 pair,不過很顯然的這樣寫起來會很麻煩,這時候就輪到 tuple 派上用場了!tuple 的概念與 pair 差不多,不過能夠存放更多的資料不限於兩個。這個容器主要用在將不同型態但又有關聯的資料結合在一起,存取的寫法跟過往的容器或型態有較大的不同,撰寫上較需特別留意。

初始化

tuple 作為 std 中的一員,在使用前需要引入 <tuple> 才能使用,當然如果你已經引入 <bits/stdc++.h> 就不需要再引入了。

#include <tuple>

今天我們假設要記錄一位學生的資訊,我們會需要紀錄他的座號、姓名以及考試的分數。這裡總共有三個項目,且都是不同的資料型態,這時就可以這樣寫:

tuple<int, string, float> t1; // 宣告但未初始化
tuple<int, string, float> t2(14, "YD", 94.5); // 宣告並初始化

首先在最前面寫上 tuple ,接著在角括號中填入要儲存的資料型態,緊接著 t 是這個 tuple 的名稱。後方的括號可寫也可不寫,主要是用來在宣告時做初始化用的,若資料在宣告後才會獲取,也可以於事後透過下面的方法存入:

t = make_tuple(14, "YD", 94.5);

或者是使用 {} 來代替 make_tuple

t = {14, "YD", 94.5};

這樣就可以將資料存入 tuple 中,不過也跟 pair 一樣,必須要注意型態是否明確,否則可能會出現錯誤。

存取與修改

tuple 的讀取方式跟過往的讀取有比較大的差異,其中一種做法就是使用 get 函式,我們需在角括號中填入存取的位置(跟陣列的 index 一樣都是從 0 開始),以及於小括號中填入 tuple 的名稱。具體寫法如下:

get<位置>(名稱)

舉例來說,若我們要讀取 t 中的前兩筆資料,可以這樣寫:

tuple<int, string, float> t(14, "YD", 94.5);

cout << get<0>(t) << endl; // 14
cout << get<1>(t) << endl; // YD

若要修改 tuple 中的資料,可以使用 get 函式取出資料後再進行修改:

tuple<int, string, float> t(14, "YD", 94.5);

get<0>(t) = 15;
get<1>(t) = "Dio";

cout << get<0>(t) << endl; // 15
cout << get<1>(t) << endl; // Dio

或許看起來很不直覺?為什麼明明是 get 函式卻可以直接修改資料呢?這是因為 get 函式實際上是回傳一個 reference,所以我們可以透過這個 reference 來修改資料。

解構 tuple

就跟 pair 一樣,若我們想要一次讀取 tuple 中的所有資料,我們可以使用 tie 函式,這會將 tuple 中的資料解構依序塞入 tie 中的各個變數中,每個變數的型態都要與宣告時相同:

// 先宣告變數
int a;
string b;
float c;

// 解構 tuple
tie(a, b, c) = t;

有時有些數值我們並沒有要使用,這時我們就可以使用 ignore 來忽略該數值,這樣就可以避免宣告過多無意義變數:

// 如果不需要中間的數值
tie(a, ignore, b) = t;

cout << a << ' ' << b << endl; // 1 3

tuple 的比較

tuple 也可以進行比較,不過要注意的是 tuple 的比較是從第一個元素開始比較,若第一個元素相同則比較第二個元素,一直往後比較到發現不同為止。若所有元素都相同則兩個 tuple 判斷為相等。若要比較 tuple ,可以使用 ==!=<><=>= 這些運算子:

tuple<int, int, int> t1(1, 2, 3);
tuple<int, int, int> t2(1, 3, 3);
tuple<int, int, int> t3(2, 2, 3);

cout << (t1 == t2) << endl; // false
cout << (t1 != t2) << endl; // true
cout << (t1 < t2) << endl; // true
cout << (t1 > t2) << endl; // false
cout << (t1 <= t2) << endl; // true

取得 tuple 的元素數量

如果你想知道 tuple 中有幾個元素,可以使用 tuple_size 這個函式:

tuple<int, string, float> t(14, "YD", 94.5);

cout << tuple_size<decltype(t)>::value << endl; // 3

decltype 是一個用來取得變數型態的函式,這邊的 decltype(t) 就是取得 t 的型態,換句話說我們也可以這樣寫:

cout << tuple_size<tuple<int, string, float>>::value << endl; // 3

小測驗

下列哪個作法可以取得 tuple<int, int, int> t = {1, 2, 3}; 中的第二個元素(也就是 2)?