std::set
std::setは, 順序集合です. 順序集合の要素を保存することが可能です.利用するには, 比較演算オペレータ<が定義されていなければなりません.
例: グローバル関数を準備
#include <set> ... class MYF { public: int val,opt; MYF(int i,int j){val=i;opt=j;} }; bool operator<(const MYF& lhs,const MYF& rhs){return lhs.val < rhs.val;}; ... int main(){ std::set<MYF> myset; myset.emplace(5,1); myset.emplace(2,3); myset.emplace(-3,5); myset.emplace(2,7); for(auto& e:myset) std::cout << e.val << "." << e.opt << std::endl; };
この場合だと, 出力は
-3.5 2.3 5.1
になり, ここの2, 7はねぐられます. なぜならば, ここで定義した比較演算<では, MYF.val だけで大小を決めているので, ここの2,3と同じものであると判断されたからです.
例: 比較クラスを準備
#include <set> ... class MYF { public: int val,opt; MYF(int i,int j){val=i;opt=j;} }; class whichLess { public: constexpr bool operator()(const MYF& lhs, const MYF& rhs) const {return lhs.key > rhs.key;}; }; int main(){ std::set<MYF,whichLess> myset; ...
この比較クラスは, keyが大きい方を「小さい」と定義しちゃってるので, keyが大きい順に集合が並べられます.
例: 比較関数を準備
#include <set> ... class MYF { public: int val,opt; MYF(int i,int j){val=i;opt=j;} }; class myTestClass(){ public: static bool comComPa(const MYF& lhs,const MYF& rhs) { return lhs.key < rhs.key; }; std::set<MYF,bool(*)(const MYF&,const MYF&)> myset {comComPa}; ...
ううむ.このサンプルが,どういう理屈で動作するのか理解できない・・・てゆうか, 最後の{comComPa}が何なのか,だれかここにきて説明しろ
集合系のコンテナ要素はwrite-protectedですよ
集合系のコンテナの最大の特徴は,(key,value)ペアではなく, (key) の集合であるということです.集合に要素を追加した後で, key を変更することはできません!
for(auto& e:myset) e.value+=1;
set.cpp:90:11: error: cannot assign to variable 'e' with const-qualified type 'const MYF &'
このeはMYFオブジェクトですので,比較に使っているkey以外にvalueもあるのですが,集合に追加した要素はwrite-protectかかっているので,書き換えることはできません.
要素がwrite-protectedなのであって, 集合自身はwrite-protectではありません. 要素の追加削除は可能です.要素の値を変更することができないだけです.
std::unordered_set
std::unordered_setは, 順序を持たない集合です. なんのためにあるのかというと, std::setは, 要素の追加挿入をするたびに,要素のソートを行うので,要素が増えると問題になるんですね.で,要素の並び順に興味を持たないユーザーには,ただ遅いだけになるわけです.std::unordered_setは,ソートを省略したので, 追加挿入が少し早い.
あくまでソートしないだけなので,同一の要素を多数保存することはできません. 整数や不動小数点数など,等号が定義されているもののstd::unordered_setは簡単です:
例: いや普通に簡単だろう
#include <unordered_set> ... class myTestClass(){ public: std::unordered_set<int> myset; ...
デフォで等号が定義されていない,あんたのクラスには,少しばかり定義するものがあります:
例: ハッシュクラス・等値クラスを準備
#include <set> ... class MYF { public: int val,opt; MYF(int i,int j){val=i;opt=j;} }; class myHash { public: constexpr size_t operator()(const MYF& key) const {return key.key;}; }; class myEqual { public: constexpr size_t operator()(const MYF& lhs,const MYF& rhs) const {return lhs.key == rhs.key;}; }; int main(){ std::set<MYF,myHash,myEqual> myset;
一応何かで整理する必要があるので, Hash値を計算するクラスが必要です.で,値が等しいとは何か?を定義する必要があります.
std::multiset
順序はあるのだが, 同一の値を多数保持できるstd::multiset もあります.基本的には std::set と同じですが
bool operator <(const myClass& lhs, const myClass& rhs) { return lhs.key < rhs.key; }; class Multi_Test { public: std::multiset<myClass> d0; Multi_Test() { d0.emplace(3,0.3); d0.emplace(1,0.3); d0.emplace(-5,0.2); d0.emplace(1,0.1); print(); } void print() { int i=0; for(auto& e:d0) std::cout << "d0[" << i++ << "] KEY[" << e.key << "]= " << e.value << std::endl; } };
このような感じでは,
d0[0] KEY[-5]= 0.2 d0[1] KEY[1]= 0.3 d0[2] KEY[1]= 0.1 ⇦重複したキーも,何処かに保存される d0[3] KEY[3]= 0.3
ところが異なります.