【C++】自作クラス/構造体を std::map や std::set のキーにする方法

目次

はじめに

C++のstd::setstd::mapは、キーの大小関係に基づいて、内部のデータを自動的にソートします。intstringのような基本型は、a < b のように、コンパイラが比較方法を知っているため、そのままキーとして使えます。

しかし、自作したProductのような構造体をキーにしようとすると、「2つのProductオブジェクトのどちらが『小さい』のか分からない」とコンパイラに怒られてしまいます。

この問題を解決するには、主に以下の2つの方法があります。

  1. 比較演算子 < をオーバーロードする: そのクラスの「自然な順序」を定義する。
  2. 独自の比較オブジェクトを提供する: ソートのルールを外部から指定する。

この記事では、これら2つの方法を、具体的なサンプルコードと共に解説します。


方法1: 比較演算子 < をオーバーロードする

最もシンプルで一般的な方法です。キーとして使いたい構造体に対して、operator< を定義します。std::setstd::mapは、キーの比較が必要になると、このoperator<を自動的に見つけて利用します。

サンプルコード

#include <iostream>
#include <string>
#include <set>

using namespace std;

// 商品構造体
struct Product {
    int id;
    string name;
};

// 1. Product構造体のための operator< をグローバル関数として定義
//    idメンバーを基準に比較する
bool operator<(const Product& lhs, const Product& rhs) {
    return lhs.id < rhs.id;
}

int main() {
    // 2. 通常通り set<Product> を宣言
    set<Product> product_set;
    
    product_set.insert({102, "みかん"});
    product_set.insert({101, "りんご"});
    product_set.insert({103, "ぶどう"});
    
    // idの昇順で出力される
    for (const auto& product : product_set) {
        cout << "ID: " << product.id << ", 商品名: " << product.name << endl;
    }
    
    return 0;
}

解説: bool operator<(const Product& lhs, const Product& rhs) 関数を定義することで、Productオブジェクト同士が < 演算子で比較された際の動作(この場合はidで比較)をコンパイラに教えています。


方法2: 独自の比較オブジェクトを提供する

「ID順だけでなく、商品名順でもソートしたい」といったように、複数のソート順を使い分けたい場合や、元の構造体を変更したくない場合に有効な方法です。

比較ルールを定義した、関数のように振る舞うオブジェクト(関数オブジェクト)を作成し、setmapのテンプレート引数として渡します。

サンプルコード

#include <iostream>
#include <string>
#include <map>

using namespace std;

// 商品構造体(今回は operator< を定義しない)
struct Product {
    int id;
    string name;
};

// 1. 商品名を基準に比較するための、独自の比較オブジェクトを定義
struct CompareProductByName {
    bool operator()(const Product& lhs, const Product& rhs) const {
        return lhs.name < rhs.name;
    }
};

int main() {
    // 2. mapの3番目のテンプレート引数に、比較オブジェクトを指定
    map<Product, int, CompareProductByName> stock;
    
    stock[{102, "みかん"}] = 30;
    stock[{101, "りんご"}] = 50;
    stock[{103, "ぶどう"}] = 20;

    // 商品名のアルファベット順で出力される
    for (const auto& [product, quantity] : stock) {
        cout << "商品名: " << product.name << ", 在庫: " << quantity << endl;
    }
    
    return 0;
}

解説:

  • struct CompareProductByName { ... }: ()演算子をオーバーロードした関数オブジェクトです。
  • map<Product, int, CompareProductByName>: mapの3番目のテンプレート引数に、キーの比較に使う型を指定します。ここに自作の比較オブジェクトを指定することで、デフォルトの<の代わりに、そのオブジェクトが比較ロジックとして使われます。

使い分けのポイント

  • operator< のオーバーロード:
    • そのオブジェクトにとって「自然で、最も基本的な順序」が一つだけ決まっている場合に適しています。(例: ID順)
  • 独自の比較オブジェクト:
    • 複数の順序でソートしたい場合や、ライブラリで定義されている構造体など、元の定義を変更できない場合に適しています。

まとめ

今回は、自作した型を std::setstd::map のキーとして使うための2つの方法を解説しました。コンテナにソートのルールを教えることで、自作の型も基本データ型と同じように、連想コンテナで自在に扱えるようになります。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次