pair | Programming Place Plus C++編【標準ライブラリ】 第3章

トップページC++編

C++編で扱っている C++ は 2003年に登場した C++03 という、とても古いバージョンのものです。C++ はその後、C++11 -> C++14 -> C++17 -> C++20 と更新されており、今後も 3年ごとに更新されます。
なかでも C++11 での更新は非常に大きなものであり、これから C++ の学習を始めるのなら、C++11 よりも古いバージョンを対象にするべきではありません。特に事情がないなら、新しい C++ を学んでください。 当サイトでは、C++14 をベースにした新C++編を作成中です。

この章の概要

この章の概要です。


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);

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

問題② pair の pair を試してみてください。


解答ページはこちら

参考リンク


更新履歴

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

’2014/10/19 新規作成。



前の章へ (第2章 string)

次の章へ (第4章 STLコンテナ)

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

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る