MyUtils

View on GitHub

C++(map)についての簡易めも

久々にC++を使ったらマップの使い方を忘れていたので、簡単にまとめておく、、

基本的な使い方
挿入削除
小ネタ

map cpprefjp
参考:std::mapまとめ

基本的な使い方

#include <map>
std::map<std::string,int> salary;

/// 普通にmapにインサートされる
salary["John"] = 1400;
salary["Tom"] = 1000;
salary["Harry"] = 0;

int a = salary["John"];   // 1400
int b = salary["Tom"];    // 1000
int c = salary["Harry"];  // 0


/// 🚨参照しただけで追加されるので注意がいる。
std::map<int,int> m{};
cout << m.size() << endl; // 何も要素を追加していないので当然 0
cout << m[1] << endl;     // 要素は存在しない, 自動で0が追加される
cout << m.size() << endl; // 自動で要素が追加されているので 1

/// 🚨keyを指定して参照(atでkeyがない場合、例外が発生するので注意)
std::map<int,int> m{};
cout << m.size() << endl; // 何も要素を追加していないので当然 0
cout << m.at(1) << endl;  // 要素は存在しない, Abort! out_of_range


/// 🌟[]で自動的に値を挿入させず、例外も出さずにキーの指定をする場合
std::map<std::string, unsigned> dictionary{
    {"John", 1000},{"Tom", 1400},{"Harry", 800}
};

/// 🌟ifの中身はcountでもいける。なければ0あれば1となるので。 
/// しかし、findとcountでは意味合いが違うので可読性を考えて使うこと
// if ( dictionary.count("hoge") == 1 ) 
if ( auto iter = dictionary.find("hoge"); iter != end(dictionary) ) 
{
  std::cout << iter->second << std::endl;
} else {
  std::cout << "not exists" << std::endl;
}

/// 🌟マップのイテレーション(順番はless<Key>の順となる)
for (const auto& [key, value] : dictionary){
    std::cout << key << " => " << value << "\n";
}



mapの挿入/削除

// 🌟 insertにはstd::make_pair(,) を作って入れる
std::map<int,int> dic{};
dic.insert(std::make_pair(1,3));


std::map<int,int> dic{
    {1,2},
    {2,3},
    {3,4}
};

dic.erase(1); // (1,2)は消える
// イテレータからの削除もできる。
// dic.erase(begin(dic)); // 最初の要素(1,2)は消える

// (2,3)
// (3,4)
for (auto const& [k, v] : dic)
    printf("(%d,%d)\n", k, v); 

小ネタ

文字列比較のコストを下げる

  // 比較関数オブジェクトをデフォルトのstd::less<Key>の代わりにstd::less<> (std::less<void>と同じ) と指定する。
  // 検索関係のインタフェースに文字列リテラルを指定した際に
  // キーのためのstd::stringオブジェクトが作られないので余分なメモリ確保を回避できる
  std::map<std::string, int, std::less<>> m = {
    {"Alice", 3},
    {"Bob", 1},
    {"Carol", 4}
  };

  // 🌟以下となる
  // 文字列リテラル"Bob"からstd::stringオブジェクトが作られない
  m["Bob"] = 2;
  // 文字列リテラル"Carol"からstd::stringオブジェクトが作られない
  auto it = m.find("Carol");

mapの順番について

namespace std {
  template <
        class Key,
        class T,
        class Compare = less<Key>,
        class Allocator = allocator<pair<const Key, T> >
  >
  class map;

  namespace pmr {
    template <class Key, class T, class Compare = less<Key>>
      using map = std::map<Key, T, Compare,
                           polymorphic_allocator<pair<const Key, T>>>;  // C++17から
  }
}

mapの内部では要素がキーでソートされている。 そのためmapはキーがソートできるoperator<が定義されている型であることを要求される。 ソートの手段についてコンストラクタの第3引数で比較用のless<Key>テンプレートが展開される。

つまり、第3引数Keyクラスの比較クラスを作る。 or Keyクラスにoperator<を追加にることで任意のクラス,構造体をKeyにすることができる。 以下参照。

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

// 要素がひとつの場合
struct MyInt {
  int value;
};

 // C++20から <=>で比較演算子が定義できるようになったらしい。(どう比較してるかは知らん、、)
 //friend auto operator<=>(const MyInt&, const MyInt&) = default;
 bool operator<(const MyInt& a, const MyInt& b) noexcept {
  return a.value < b.value;
}

// 要素が複数の場合
struct Person {
  int id;
  int age;
  std::string name;
};

  // C++20から <=>で比較演算子が定義できるようになったらしい。(どう比較してるかは知らん、、)
  //friend auto operator<=>(const Person&, const Person&) = default;
bool operator<(const Person& a, const Person& b) noexcept {
  // キーとして比較したい要素を列挙する(tieでid,age,nameの順で比較してくれるらしい)
  return std::tie(a.id, a.age, a.name) < std::tie(b.id, b.age, b.name);
}

int main() {
  std::map<MyInt, int> m1 {
    {MyInt{1}, 3},
    {MyInt{2}, 1},
    {MyInt{3}, 4},
  };
  std::cout << m1[MyInt{2}] << std::endl;

  std::map<Person, int> m2 {
    {Person{1, 18, "Alice"}, 3},
    {Person{2, 30, "Bob"}, 1},
    {Person{3, 30, "Carol"}, 4},
  };
  std::cout << m2[Person{2, 30, "Bob"}] << std::endl;
}
#include <iostream>
#include <map>
#include <string>
#include <tuple>

// 要素がひとつの場合
struct MyInt {
  int value;
};

struct MyIntLess {
  bool operator()(const MyInt& a, const MyInt& b) const noexcept {
    return a.value < b.value;
  }
};

// 要素が複数の場合
struct Person {
  int id;
  int age;
  std::string name;
};

struct PersonLess {
  bool operator()(const Person& a, const Person& b) const noexcept {
    // キーとして比較したい要素を列挙する
    return std::tie(a.id, a.age, a.name) < std::tie(b.id, b.age, b.name);
  }
};

int main() {
  std::map<MyInt, int, MyIntLess> m1 {
    {MyInt{1}, 3},
    {MyInt{2}, 1},
    {MyInt{3}, 4},
  };
  std::cout << m1[MyInt{2}] << std::endl;

  std::map<Person, int, PersonLess> m2 {
    {Person{1, 18, "Alice"}, 3},
    {Person{2, 30, "Bob"}, 1},
    {Person{3, 30, "Carol"}, 4},
  };
  std::cout << m2[Person{2, 30, "Bob"}] << std::endl;
}