C++編【標準ライブラリ】 第3章 pair

先頭へ戻る

このエントリーをはてなブックマークに追加

この章の概要

この章の概要です。

pair

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;                 // 各要素のデフォルトコンストラクタで初期化
	mypair b(10, 3.5);        // 初期値を与える
	mypair c(b);              // 他の pair から生成 (コピーコンストラクタ)

	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>
pair<T1, T2>::pair() :
	first(T1(), T2())
{}

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 へ変換

make_pair による生成

std::make_pair関数 を使って、pair のオブジェクトを生成することもできます。

std::pair<int, double> a = std::make_pair(10, 3.5);

C++11 で auto(【言語解説】第2章)を使えば、より簡潔に「auto 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()
{
	func(std::make_pair(10, "abc"));
	func(std::pair<int, std::string>(10, "abc"));
}

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;

	b = a;
	c = b;

	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;

	mypair a(10, 3.5);
	mypair b(10, 5.0);

	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" という形式で出力する汎用的な関数を作成して下さい。

問題A pair の pair を試してみて下さい。


解答ページはこちら

参考リンク

更新履歴

'2017/6/1 「初期化」の項の一部を、「make_pair による生成」に分離。

'2014/10/19 新規作成。



前の章へ

次の章へ

C++編のトップページへ

Programming Place Plus のトップページへ