Time Limit: 0 msec / Memory Limit: 0 KB
キーポイント
pair
- pair型は2つの値の組を表す
pair<値1の型, 値2の型> 変数名;
で宣言する変数名.first
で1番目の値、変数名.second
で2番目の値にアクセスできるmake_pair(値1, 値2)
でpairを生成することができるtie(変数1, 変数2) = pair型の値;
でpairを分解することができる
tuple
- tuple型は複数個の値の組を表す
tuple<値1の型, 値2の型, 値3の型, (...)> 変数名;
(必要な分だけ型を書く)で宣言するmake_tuple(値1, 値2, 値3, (...))
でtupleを生成することができるtie(変数1, 変数2, 変数3, (...)) = tuple型の値;
でtupleを分解することができる
pair/tupleの比較
- 1番目の値から比較され、等しい場合は次の値で比較される
auto
- 変数宣言や範囲for文において、autoを用いることで、型を省略して書くことができる
- autoで参照を作ることもできる
pair/tuple
pair
pairは2つの値の組を表す型です。
サンプルコード
#include <bits/stdc++.h> using namespace std; int main() { pair<string, int> p("abc", 3); cout << p.first << endl; // abc p.first = "hello"; cout << p.first << endl; // hello cout << p.second << endl; // 3 p = make_pair("*", 1); string s; int a; tie(s, a) = p; cout << s << endl; // * cout << a << endl; // 1 }
実行結果
abc hello 3 * 1
宣言・初期化
pair<型1, 型2> 変数名; pair<型1, 型2> 変数名(値1, 値2);
アクセス
変数名.first // 1つ目の値 変数名.second // 2つ目の値
pairの生成
make_pair(値1, 値2)
make_pairによってpair型の値を得ることができます。
また、次のようにすることでもpairを生成することができます。
pair<型1, 型2>(値1, 値2)
どちらを使っても構いませんが、make_pair
では型を明示的に書く必要がないのでmake_pair
を使うとよいでしょう。
細かい話で扱う型エイリアスを用いてpairの型に簡単な名前を付ける場合、後者の方が楽に書ける場合もあります。
pairの分解
型1 変数1; 型2 変数2; tie(変数1, 変数2) = pair型の値;
変数1、変数2にそれぞれpairの1番目の値、2番目の値が代入されます。
tuple
tupleはpairを一般化したもので、「複数個の値の組」を表す型です。
サンプルコード
#include <bits/stdc++.h> using namespace std; int main() { tuple<int, string, bool> data(1, "hello", true); get<2>(data) = false; cout << get<1>(data) << endl; // hello data = make_tuple(2, "WORLD", true); int a; string s; bool f; tie(a, s, f) = data; cout << a << " " << s << " " << f << endl; // 2 WORLD 1 }
実行結果
hello 2 WORLD 1
宣言・初期化
tuple<型1, 型2, 型3, ...(必要な分だけ型を書く)> 変数名; tuple<型1, 型2, 型3, ...(必要な分だけ型を書く)> 変数名(値1, 値2, 値3, ...); // 初期化
tupleでは0個以上の組を表現することができます。
アクセス
get<K>(tuple型の変数) // K(定数)番目にアクセス
Kは定数でなければならないことと、Kは0から始まる数であることに注意してください。
次のように、Kとして変数を用いることはできません。
int i = 2; tuple<int, int, int> data(1, 2, 3); cout << get<i>(data) << endl; // コンパイルエラー
tupleの生成
make_tuple(値1, 値2, 値3, ...)
以下のようにして生成することもできます。
tuple<型1, 型2, 型3, ...(必要な分だけ書く)>(値1, 値2, 値3, ...)
tupleの分解
型1 変数1; 型2 変数2; 型3 変数3; ︙ tie(変数1, 変数2, 変数3, ...) = tuple型の値;
pair/tupleの比較
型が同じpairやtuple同士は比較することができます。
例えばpair<int, int>
を比較する場合、1番目の値を基準に比較され、もし1番目の値が等しい場合は2番目の値を基準に比較されます。
#include <bits/stdc++.h> using namespace std; int main() { pair<int, int> a(3, 1); pair<int, int> b(2, 10); // 3 > 2 なので a < b は false if (a < b) { cout << "a < b" << endl; } else { cout << "a >= b" << endl; } pair<int, int> c(5, 1); pair<int, int> d(5, 10); // 5 == 5 であり 1 < 10 なので c < d は true if (c < d) { cout << "c < d" << endl; } else { cout << "c >= d" << endl; } }
実行結果
a >= b c < d
tupleを比較する場合もpairと同様に「1番目の値が最優先で比較され、等しい場合は2番目の値で比較する。もし2番目の値も等しい場合は3番目の値を比較する……」と、1番目の値から順に比較されます。
なお、==
は全ての値が等しい場合、!=
は1つ以上の異なる値が存在する場合にtrueとなります。
pairやtupleの配列をソートする場合も上記の比較順序でソートされます。
#include <bits/stdc++.h> using namespace std; int main() { vector<tuple<int, int, int>> a; a.push_back(make_tuple(3, 1, 1)); a.push_back(make_tuple(1, 2, 100)); a.push_back(make_tuple(3, 5, 1)); a.push_back(make_tuple(1, 2, 3)); sort(a.begin(), a.end()); for (tuple<int, int, int> t : a) { int x, y, z; tie(x, y, z) = t; cout << x << " " << y << " " << z << endl; } }
実行結果
1 2 3 1 2 100 3 1 1 3 5 1
auto
初期化を伴って変数を宣言する場合や範囲for文において、型の部分にauto
と書くことによって型を省略することができます。
サンプルコード
#include <bits/stdc++.h> using namespace std; string concat(string a, string b) { return a + b; } int main() { string a = "Hello,"; string b = "World"; auto ab = concat(a, b); // string型 cout << ab << endl; vector<int> c = {1, 2, 3}; auto d = c; // vector<int>型 for (auto elem : d) { // elemはint型 cout << elem << endl; } }
実行結果
Hello,World 1 2 3
変数宣言でのauto
auto 変数名 = 初期値;
範囲for文でのauto
for (auto 変数名 : 配列変数) { // 変数名 を使う }
参照でのauto
int a = 12345; auto &ref = a; // aへの参照 `int &ref = a;`と書くのと同じ vector<int> b = {1, 2, 3}; // 範囲for文でも参照にすることができる for (auto &elem : b) { elem *= 2; } cout << b.at(0) << endl; // 2 cout << b.at(1) << endl; // 4 cout << b.at(2) << endl; // 6
細かい話
ignore
pairやtupleを分解する際に要らない要素を捨てたい場合、ignore
をtieの引数に渡すことで、対応する位置の値を捨てることができます。
#include <bits/stdc++.h> using namespace std; int main() { pair<int, int> p(3, 5); int right; tie(ignore, right) = p; // 2番目の値だけ取り出す cout << right << endl; // 5 tuple<int, string, bool> tpl(2, "hello", false); int x; string s; tie(x, s, ignore) = tpl; // 1番目の値、2番目の値だけ取り出す cout << x << " " << s << endl; // 2 hello }
実行結果
5 2 hello
型エイリアス
型エイリアスを用いると型に別の名前をつけることができます。 pairやtupleなど型名が長くなってしまう場合に型エイリアスを使うと便利です。
#include <bits/stdc++.h> using namespace std; int main() { using pii = pair<int, int>; // これ以降 pii という型名はpair<int, int> と同じ意味で使える pii p; p = make_pair(1, 2); cout << "(" << p.first << ", " << p.second << ")" << endl; p = pii(3, 4); // 別のpairを作って代入 cout << "(" << p.first << ", " << p.second << ")" << endl; }
実行結果
(1, 2) (3, 4)
次のようにして型エイリアスを宣言することができます。
型エイリアスの宣言
using 新しい型名 = 型名;
宣言した型エイリアスは型名として利用できます。
型エイリアスを用いることで、多次元配列の型を簡単に書くこともできます。
using vi = vector<int>; // intの1次元の型に vi という別名をつける using vvi = vector<vi>; // intの2次元の型に vvi という別名をつける int N = 10, M = 20; vvi data(N, vi(M)); // N * M の2次元配列
これまでプログラムの2行目に書いていたusing namespace std;
にもusing
というキーワードが出てきますが、このusing
と型エイリアスのusing
は違う意味なので注意してください。using namespace std;
については4章で説明します。
なお、C++では同様の機能としてtypedef
宣言が用意されていますが、基本的にはusing
を使うようにしましょう。
#include <bits/stdc++.h> using namespace std; int main() { typedef pair<int, int> pii; // pair<int, int> に pii という別名を付ける pii p = make_pair(1, 2); cout << "(" << p.first << ", " << p.second << ")" << endl; }
実行結果
(1, 2)
演習問題
リンク先の問題を解いてください。
ABCの問題
今回紹介した機能を用いて解くことができるAtCoder Beginner Contestの問題を紹介します。 練習問題だけでは物足りない人は是非挑戦してみてください。