C++編で扱っている C++ は 2003年に登場した C++03 という、とても古いバージョンのものです。C++ はその後、C++11 -> C++14 -> C++17 -> C++20 -> C++23 と更新されています。
なかでも C++11 での更新は非常に大きなものであり、これから C++ の学習を始めるのなら、C++11 よりも古いバージョンを対象にするべきではありません。特に事情がないなら、新しい C++ を学んでください。 当サイトでは、C++14 をベースにした新C++編を作成中です。
この章の概要です。
pair は、2つの型の異なる(あるいは同じ)値をまとめて管理するクラステンプレート📘(【言語解説】第20章)です。使用するためには、<utility> という標準ヘッダをインクルードする必要があります。
pair のメンバ変数📘に関わる部分を具体的に取り上げると、次のようになっています。
namespace std {
template <typename T1, typename T2>
struct pair {
typedef T1 first_type;
typedef T2 second_type;
;
T1 first;
T2 second};
}
テンプレート仮引数 T1、T2 に当てはめた型に応じて、2つのメンバ変数 first、second が定義されるのが分かると思います。このように、2つの任意の型の変数をまとめて管理することが、pair の役割です。
class ではなく struct で定義されているので、デフォルトのアクセス指定子📘は public になります。そのため、この実装例で登場するすべてのメンバは「公開」されています。したがって、first や second へは、外部からでも直接アクセスできます。
pair をインスタンス化してみます。
#include <iostream>
#include <utility>
int main()
{
typedef std::pair<int, double> mypair;
; // 各要素のデフォルトコンストラクタで初期化
mypair a(10, 3.5); // 初期値を与える
mypair b(b); // 他の pair から生成 (コピーコンストラクタ)
mypair c
std::cout << a.first << ", " << a.second << "\n"
<< b.first << ", " << b.second << "\n"
<< c.first << ", " << c.second << std::endl;
}
実行結果:
0, 0
10, 3.5
10, 3.5
引数がないコンストラクタを使用した場合、2つの要素 first、second は、テンプレート実引数の型が持つデフォルトコンストラクタによって初期化されます。これはつまり、次のように実装されているということです。
template <typename T1, typename T2>
<T1, T2>::pair() :
pair(T1(), T2())
first{}
T1 や T2 がクラス型で、そのデフォルトコンストラクタがメンバ変数を初期化していない場合を除けば、未初期化状態にはなり得ないということになります。
引数が2つのコンストラクタは、第1引数が first、第2引数が second の初期値になります。
3つ目はコピーコンストラクタです。pair のコピーコンストラクタは、テンプレートコンストラクタ(【言語解説】第22章)になっており、暗黙の型変換📘が行われます。たとえば、次のように使えます。
std::pair<int, const char*> p1(100, "abc");
std::pair<int, std::string> p2(p1); // const char* から std::string へ変換
std::make_pair関数 を使っても、pair のオブジェクト📘を生成できます。
std::pair<int, double> a = std::make_pair(10, 3.5);
std::make_pair関数は、2つの引数を持ち、それぞれ first と second の値を指定します。関数テンプレート📘なので、実引数に渡した値から、テンプレート実引数を推測してくれます(【言語解説】第9章)。そのため、pair のテンプレート仮引数を具体的に指示しなくても良いのです。
std::make_pair関数を使うと、具体的な型名を記述しなくて済むため、記述を簡潔にする効果があります。たとえば、次のように使えます。
void func(std::pair<int, std::string> p);
int main()
{
(std::make_pair(10, "abc"));
func(std::pair<int, std::string>(10, "abc"));
func}
2つ目の呼び出し方では、長い記述が必要になります。また、型名を func関数の宣言と同じことをあらためて記述しなければならないことも欠点です。
このように std::make_pair関数は便利なユーティリティですが、型を推測によって決めているので、明確に型を指定しなければならない場面では、使い方に気を付ける必要があります。たとえば、次の2つの文は、異なる結果になります。
std::pair<int, float>(10, 3.5); // 明示的に float型を指定しているので 3.5 は 3.5f
std::make_pair(10, 3.5); // 推測により second は double型
pair の代入は、シンプルに =演算子で行います。
#include <iostream>
#include <utility>
#include <string>
int main()
{
std::pair<int, const char*> a(10, "abc");
std::pair<int, const char*> b;
std::pair<int, std::string> c;
= a;
b = b;
c
std::cout << a.first << ", " << a.second << "\n"
<< b.first << ", " << b.second << "\n"
<< c.first << ", " << c.second << std::endl;
}
実行結果:
10, abc
10, abc
10, abc
このサンプルプログラムで、「b = a;」の部分は、a も b も同じ型なので普通に代入できるのは自然だと思いますが、c = b;
では、両者の型が異なります。実は、代入演算子がメンバ関数テンプレートとしてオーバーロード📘されているため、暗黙的に型変換してくれます。
pair 同士で比較できます。
#include <iostream>
#include <utility>
int main()
{
typedef std::pair<int, double> mypair;
(10, 3.5);
mypair a(10, 5.0);
mypair b
std::cout << std::boolalpha
<< (a == b) << "\n"
<< (a != b) << "\n"
<< (a < b) << "\n"
<< (a <= b) << "\n"
<< (a > b) << "\n"
<< (a >= b) << std::endl;
}
実行結果:
false
true
true
true
false
false
pair 同士の比較では、first の大小関係の方が優先され、second の優先度は低くなります。つまり、<、<=、>、>= では、first の値が異なる場合は、その大小関係が結果になりますが、first の値が同じ場合は、second の大小関係が結果になります。
問題① たとえば 100 と “abc” という値のペアを管理する pair があるとき、標準出力へ “100, abc” という形式で出力する汎用的な関数を作成してください。
問題② pair の pair を試してみてください。
Programming Place Plus のトップページへ
はてなブックマーク に保存 | Pocket に保存 | Facebook でシェア |
X で ポスト/フォロー | LINE で送る | noteで書く |
![]() |
管理者情報 | プライバシーポリシー |