新トップページへ | Tip

STLの基本的な使い方

LastUpdate : 13/01/02

STLを使用するにあたり、基本的な内容をまとめています。
Javaなどをやったことがあるなら、すぐ馴染めると思います。STLと特別に名前がついていますが、簡単に言うとライブラリ関数がいっぱいあるよ〜ってだけの話ですから。
ここに載せましたソースは、VisualStudio2012にて、実行できることを確認しています(C++11を前提としています)。


もくじ

STLが提供するものについて
シーケンスコンテナについて
vectorコンテナ
listコンテナ
dequeコンテナ
連想コンテナについて
mapコンテナ
multimapコンテナ
setコンテナ
multisetコンテナ
コンテナアダプタについて
queueコンテナ
priority_queueコンテナ
stackコンテナ
アルゴリズムについて(個人的によく使いそうかな?というのをいくつかピックアップしました)
コピー(copy)
検索(find/find_if)
並び替え(sort)
二分検索(binary_search)
transformについて(transform)
並び順の変更(rotateとrandom_shuffle)
マージ処理(merge)
最大値・最少値の検索(max_element/min_element)
for_eachアルゴリズム(for_each)
関数オブジェクト
バインダ
否定回路
関数アダプタ

STLが提供するものについて

以下の表にまとめました。

種類 説明
コンテナ 汎用的なデータの格納クラス
アルゴリズム コンテナに格納されたデータに対する処理
反復子 コンテナに対する汎用的なデータ取得クラス。以下のような種別がある。
  ・ランダムアクセス反復子(値を取得または設定が可能。ランダムアクセスが可能)
  ・双方向反復子(値を取得または設定が可能。前方向、後ろ方向に移動可能)
  ・前方反復子(値を取得または設定が可能。前方向のみ移動可能)
  ・入力反復子(値を取得可能。前方向のみ移動可能)
  ・出力反復子(値を設定可能。前方向のみ移動可能)
アロケータ コンテナに対しメモリ割り当てを行う機能。デフォルトの実装が用意されているので、通常それを使う。
自分でアロケータを作成し、それを用いることも可能。
関数オブジェクト operator()が定義されているクラス。
関数ポインタのようなもの。
アダプタ オブジェクトを変換する。
コンテナアダプタ、反復子アダプタ、関数アダプタがある。

これらを組み合わせて処理を記述します。


vectorコンテナ

std::vectorの使用例です。
vecotrは要素の取得にatというメンバ関数が用意されています。また、[ ]でも取得可能です。
無論、vector内で要素の保存に必要なメモリ領域はvector内で必要によって拡張されていきます。

Stl1.cpp
#include <string>
#include <iostream>

#include <vector>       // vectorを使用するのに必要

class Stl1 {
public:
        Stl1(std::string myName) {
                std::cout << "Stl1コンストラクタ!\n";
                setName(myName);
        }
        ~Stl1() { 
                std::cout << "Stl1ディストラクタ!\n";
        }

        void setName(std::string myName)  { this->myName = myName; }
        std::string getName() const { return myName; }
private:
        std::string myName;
};

void stl1() {

        // vectorの作成
        std::vector<Stl1*> v1;
        std::vector<int> v2;

        // 要素の一番後ろに追加する
        v1.push_back(new Stl1("1個目の要素"));
        v1.push_back(new Stl1("2個目の要素"));

        // 要素の一番後ろに追加する
        v2.push_back(100);
        v2.push_back(200);
        v2.push_back(300);

        // v1の内容を表示する
        std::cout << "■v1の内容を表示\n";
        for (unsigned int i=0; i<v1.size(); i++) {
                std::cout << i << " : " << v1[i]->getName() << "\n";    // []でアクセスしてもatメンバ関数でも
                std::cout << i << " : " << v1.at(i)->getName() << "\n"; // どちらも使用可能
                delete v1[i];   // ここでdeleteしている理由は特になし。不要になったらdeleteすればよい。
        }

        // v2の内容を表示する
        std::cout << "■v2の内容を表示\n";
        for (unsigned int i=0; i<v2.size(); i++) {
                std::cout << i << " : " << v2[i] << "\n";
                std::cout << i << " : " << v2.at(i) << "\n";
        }

        // vectorの要素を消すにはclearを呼びます
        v1.clear();
        v2.clear();
        std::cout << "v1の要素数 : " << v1.size() << "\n";
        std::cout << "v2の要素数 : " << v2.size() << "\n";

        std::cout << "\nstl1終了!\n";
}


stl1関数実行結果
Stl1コンストラクタ!
Stl1コンストラクタ!
■v1の内容を表示
0 : 1個目の要素
0 : 1個目の要素
Stl1ディストラクタ!
1 : 2個目の要素
1 : 2個目の要素
Stl1ディストラクタ!
■v2の内容を表示
0 : 100
0 : 100
1 : 200
1 : 200
2 : 300
2 : 300
v1の要素数 : 0
v2の要素数 : 0

stl1終了!


listコンテナ

std::listコンテナの使い方についてです。
listは、STLが提供するsortが使えません。その代り、list自身に、sortメンバ関数を持っています。そのため、ソートしたい場合はlistのメンバ関数sortを用います。

stl2_1では、よく使いそうなメンバ関数をいくつか取り上げました。
stl2_2では、list::sortの簡単な使い方の例を挙げました。

Stl2.cpp
#include <string>
#include <iostream>

#include <list> // listを使用するのに必要
#include <functional>   // lessを使用するのに必要


class Stl2 {
public:
        Stl2(std::string myName, int order) {
                setName(myName);
                setOrder(order);
                //std::cout << "Stl2の「" << getName() + "」コンストラクタ!" << "\n";
        }
        ~Stl2() {
                //std::cout << "Stl2の「" << getName() + "」ディストラクタ!" << "\n";
        }
        Stl2(Stl2& obj) {
                setName(obj.getName());
                setOrder(obj.getOrder());
                //std::cout << "Stl2の「" << getName() + "」コピーコンストラクタ!" << "\n";
        }

        void setName(std::string myName)  { this->myName = myName; }
        std::string getName() const { return myName; }
        void setOrder(int order) { this->order = order; }
        int getOrder() const { return order; }
private:
        std::string myName;
        int order;
};


// list::sortに渡す要素の並び順判定処理
bool listOriginalSort(Stl2& left, Stl2& rigth) {
        return left.getOrder() > rigth.getOrder();
}

// listの使い方の例
void stl2_1() {
        std::list<Stl2> lst1;
        std::list<Stl2> lst2;

        // lst1に要素を後ろへ追加する
        lst1.push_back(Stl2("1番目の要素です", 1));
        lst1.push_back(Stl2("2番目の要素です", 2));
        lst1.push_back(Stl2("3番目の要素です", 3));
        // lst2に要素を後ろへ追加する
        lst2.push_back(Stl2("4番目の要素です", 4));
        lst2.push_back(Stl2("6番目の要素です", 6));


        // ■要素の追加(insert)のサンプル
        // lst2の2番目に要素を追加する
        std::list<Stl2>::iterator lst2InsertIterator = lst2.begin();
        // 2番目にイテレーターを移動する
        lst2InsertIterator++;   // 2番目へ移動
        lst2.insert(lst2InsertIterator, Stl2("5番目の要素です", 5));

        // ■Iteratorのサンプル
        std::cout << "lst1の要素を表示する\n";
        std::list<Stl2>::iterator lst1Iterator = lst1.begin();
        while (lst1Iterator != lst1.end()) {
                std::cout << "lst1 : " << lst1Iterator->getName() << "\n";
                lst1Iterator++; // 次の要素へ移動する
        }
        std::cout << "lst2の要素を表示する\n";
        std::list<Stl2>::iterator lst2Iterator = lst2.begin();
        while (lst2Iterator != lst2.end()) {
                std::cout << "lst2 : " << lst2Iterator->getName() << "\n";
                lst2Iterator++; // 次の要素へ移動する
        }

        // ■spliceのサンプル(lst1の後ろに、lst2の要素すべてを移動する)
        lst1.splice(lst1.end(), lst2);
        std::cout << "spliceをした後のlst1の要素を表示する\n";
        std::list<Stl2>::iterator lst1Iterator2 = lst1.begin();
        while (lst1Iterator2 != lst1.end()) {
                std::cout << "lst1 : " << lst1Iterator2->getName() << "\n";
                lst1Iterator2++;        // 次の要素へ移動する
        }
        std::cout << "spliceをした後のlst2の要素数を表示する\n";
        std::cout << "lst2 count : " << lst2.size() << "\n";

        // ■sortのサンプル(lst1を降順にソートする
        lst1.sort(listOriginalSort);    // ソートの際の判定処理を自分で作成したOriginalSortを指定する
        std::cout << "sort後のlst1の内容を表示する\n";
        std::list<Stl2>::iterator lst1Iterator3 = lst1.begin();
        while (lst1Iterator3 != lst1.end()) {
                std::cout << "lst1 : " << lst1Iterator3->getName() << "\n";
                lst1Iterator3++;        // 次の要素へ移動する
        }

        // ■eraseのサンプル(lst1から特定の要素を削除する)
        std::list<Stl2>::iterator lst1IteratorErase = lst1.begin();
        // 3番目の要素を削除するため、イテレーターを2個進める
        lst1IteratorErase++;
        lst1IteratorErase++;
        lst1.erase(lst1IteratorErase);
        std::cout << "lst1から3番目の要素を削除した結果を表示する\n";
        std::list<Stl2>::iterator lst1Iterator4 = lst1.begin();
        while (lst1Iterator4 != lst1.end()) {
                std::cout << "lst1 : " << lst1Iterator4->getName() << "\n";
                lst1Iterator4++;        // 次の要素へ移動する
        }

        // ■clearのサンプル(lst1からすべての要素を削除する)
        std::cout << "clearのサンプル\n";
        std::cout << "lst1のclear実行前の要素数 : " << lst1.size() << "\n";
        std::cout << "lst1のempty実行結果 : " << lst1.empty() << "\n";
        lst1.clear();
        std::cout << "lst1のclear実行後の要素数 : " << lst1.size() << "\n";
        std::cout << "lst1のempty実行結果 : " << lst1.empty() << "\n";

        std::cout << "\nstl2_1関数が終了しました!";
}

// list::sortについて。簡単なケースの例。
// listに格納する値が数値や文字列の場合、引数を何も指定せず
void stl2_2() {
        std::list<int> lst1;
        
        // lst1に要素を後ろへ追加する
        lst1.push_back(10);
        lst1.push_back(20);
        lst1.push_back(30);
        lst1.push_back(40);
        lst1.push_back(50);

        // sort処理を呼び出す

        // 引数なしで呼び出すと昇順でソートされる
        //lst1.sort();
        // 引数にアルゴリズムを指定することも可能。greaterは降順ソートとなる。
        // STLもたくさんのアルゴリズムを提供しているため、目的に合ったものがあれば自作する必要はない
        lst1.sort(std::greater<int>());

        std::list<int>::iterator lst1Iterator = lst1.begin();
        std::cout << "sort処理後のlst1の要素を表示\n";
        while (lst1Iterator != lst1.end()) {
                std::cout << "lst1 : " << *lst1Iterator << "\n";
                lst1Iterator++; // 次の要素へ移動する
        }

        std::cout << "\nstl2_2関数が終了しました!";
}


stl2_1関数の実行結果
lst1の要素を表示する
lst1 : 1番目の要素です
lst1 : 2番目の要素です
lst1 : 3番目の要素です
lst2の要素を表示する
lst2 : 4番目の要素です
lst2 : 5番目の要素です
lst2 : 6番目の要素です
spliceをした後のlst1の要素を表示する
lst1 : 1番目の要素です
lst1 : 2番目の要素です
lst1 : 3番目の要素です
lst1 : 4番目の要素です
lst1 : 5番目の要素です
lst1 : 6番目の要素です
spliceをした後のlst2の要素数を表示する
lst2 count : 0
sort後のlst1の内容を表示する
lst1 : 6番目の要素です
lst1 : 5番目の要素です
lst1 : 4番目の要素です
lst1 : 3番目の要素です
lst1 : 2番目の要素です
lst1 : 1番目の要素です
lst1から3番目の要素を削除した結果を表示する
lst1 : 6番目の要素です
lst1 : 5番目の要素です
lst1 : 3番目の要素です
lst1 : 2番目の要素です
lst1 : 1番目の要素です
clearのサンプル
lst1のclear実行前の要素数 : 5
lst1のempty実行結果 : 0
lst1のclear実行後の要素数 : 0
lst1のempty実行結果 : 1

stl2_1関数が終了しました!


stl2_2関数の実行結果
sort処理後のlst1の要素を表示
lst1 : 50
lst1 : 40
lst1 : 30
lst1 : 20
lst1 : 10

stl2_2関数が終了しました!


dequeコンテナ

std::dequeの使い方です。
std::vectorと同じような性質をもちます。たぶん、普通は使わないかと。。。
ただ、std::dequeやstd::stackの内部で使用されていたりするらしい。また、ランダムアクセス反復子が使えます。

Stl3.cpp
#include <string>
#include <iostream>

#include <deque>        // dequeを使用するのに必要


void stl3() {
        std::deque<std::string> q;

        // 末尾に要素を追加する
        q.push_back("だらしねぇ");
        q.push_back("歪みねぇ");
        q.push_back("仕方ない");

        // 先頭に要素を追加する
        q.push_front("妖精哲学の三信");

        // dequeはランダムアクセス反復子をサポートするので[ ]でもアクセス可能
        for (unsigned int i=0; i<q.size(); i++) {
                std::cout << i << " : " << q[i] << "\n";
        }

        std::cout << "\nstl3が終了しました!\n";
}


stl3関数の実行結果
0 : 妖精哲学の三信
1 : だらしねぇ
2 : 歪みねぇ
3 : 仕方ない

stl3が終了しました!


mapコンテナ

std::mapの使い方について。
mapはキー重複をしていはいけない前提で、同じキーでデータを挿入しようとしても、データが入らない様子です。

また、キーに使うクラス、キーに対応する値に対し、制約があります。
  ・キーとして使用するクラスは、<演算子の定義が必要(キーでソートする際に使うらしい・・・。コンパイラによっては、ほかの演算子も求められるケースがあるらしい)。
  ・キーに対応する値に使用するクラスには、デフォルトコンストラクタが用意されている必要がある。
(STLの世界ではそういうコンベンションなんでしょうね。ただ、一般人から見たら制約です)

Stl4.cpp
#include <string>
#include <iostream>

#include <map>  // mapを使用するのに必要

class Stl4 {
public:
        Stl4() { }
        Stl4(std::string myName) { setName(myName); }

        std::string getMyName() const { return myName; }
        void setName(std::string myName) { this->myName = myName; }
private:
        std::string myName;
};


void stl4() {
        // mapオブジェクト
        std::map<std::string, Stl4> map;

        // ■mapに挿入する要素
        std::pair<std::string, Stl4> node1("node_key_1", Stl4("その1"));
        std::pair<std::string, Stl4> node2("node_key_2", Stl4("その2"));
        std::pair<std::string, Stl4> node3("node_key_3", Stl4("その3"));
        std::pair<std::string, Stl4> node4("node_key_4", Stl4("その4"));
        std::pair<std::string, Stl4> node5("node_key_1", Stl4("その5"));  // わざとキー値を重複させてみる

        // ■mapに要素を追加する
        std::pair<std::map<std::string, Stl4>::iterator, bool> resultPair;
        resultPair = map.insert(node1);
        if (!resultPair.second)std::cout << node1.second.getMyName() << "をmapに追加失敗!\n";
        resultPair = map.insert(node2);
        if (!resultPair.second)std::cout << node2.second.getMyName() << "をmapに追加失敗!\n";
        resultPair = map.insert(node3);
        if (!resultPair.second)std::cout << node3.second.getMyName() << "をmapに追加失敗!\n";
        resultPair = map.insert(node4);
        if (!resultPair.second)std::cout << node4.second.getMyName() << "をmapに追加失敗!\n";
        resultPair = map.insert(node5);
        if (!resultPair.second)std::cout << node5.second.getMyName() << "をmapに追加失敗!\n";

        // ■mapから指定のキーの値を取得する
        // findで要素を探し、戻り値としてIteratorが返る。末尾の要素を指していなければ要素が発見できたこととなる
        std::cout << "mapから指定のキーの値を取得する\n";
        std::map<std::string, Stl4>::iterator findIterator = map.find("node_key_3");
        if (findIterator != map.end()) {
                // キーに対応する値を発見 
                std::cout << "取得した要素のキー値 : " << findIterator->first << "\n";
                std::cout << "取得した要素の値     : " << findIterator->second.getMyName() << "\n";
        } else {
                // キーに対応する値が発見できなかった
                std::cout << "要素を取得できませんでした!";
        }

        // ■mapの要素一覧を取得する
        std::cout << "mapの要素一覧を取得する\n";
        std::map<std::string, Stl4>::iterator allKeyIterator = map.begin();
        while (allKeyIterator != map.end()) {
                std::cout << "key   : " << allKeyIterator->first << "\n";
                std::cout << "value : " << allKeyIterator->second.getMyName() << "\n";
                allKeyIterator++;       // 次の要素に移動する
        }

        std::cout << "\nstl4関数が終了しました\n";
}


stl4関数の実行結果
その5をmapに追加失敗!
mapから指定のキーの値を取得する
取得した要素のキー値 : node_key_3
取得した要素の値     : その3
mapの要素一覧を取得する
key   : node_key_1
value : その1
key   : node_key_2
value : その2
key   : node_key_3
value : その3
key   : node_key_4
value : その4

stl4関数が終了しました


multimapコンテナ

std::multimapの使い方についてです。
mapと異なるのは、1つのキーに対し、複数の値が紐づくことです。それ以外はほぼmapと同じです。

Stl5.cpp
#include <string>
#include <iostream>

#include <map>  // multimapを使用するのに必要

class Stl5 {
public:
        Stl5() { }
        Stl5(std::string myName) { setName(myName); }

        std::string getMyName() const { return myName; }
        void setName(std::string myName) { this->myName = myName; }
private:
        std::string myName;
};


void stl5() {
        // multimapオブジェクト
        std::multimap<std::string, Stl5> map;

        // ■multimapに挿入する要素(その1〜その3と、その4〜その5がそれぞれキー重複している)
        std::pair<std::string, Stl5> node1("node_key_1", Stl5("その1"));
        std::pair<std::string, Stl5> node2("node_key_1", Stl5("その2"));
        std::pair<std::string, Stl5> node3("node_key_1", Stl5("その3"));
        std::pair<std::string, Stl5> node4("node_key_2", Stl5("その4"));
        std::pair<std::string, Stl5> node5("node_key_2", Stl5("その5"));

        // ■multimapへ要素を追加
        map.insert(node1);
        map.insert(node2);
        map.insert(node3);
        map.insert(node4);
        map.insert(node5);

        // ■要素を取得する
        // 処理中のmap.upper_boundは、指定されたキー値よりも大きいキーの最初の要素を返します。
        // これと不一致なことをループ継続条件としています。
        std::multimap<std::string, Stl5>::iterator findIterator = map.find("node_key_1");
        if (findIterator != map.end()) {
                do {
                        std::cout << "key : " << findIterator->first << "\n";
                        std::cout << "val : " << findIterator->second.getMyName() << "\n";
                        findIterator++; // 要素を次に進める
                } while(findIterator != map.upper_bound("node_key_1"));
        };

        std::cout << "\nstl5関数の実行が終了しました\n";
}


stl5関数の実行結果
key : node_key_1
val : その1
key : node_key_1
val : その2
key : node_key_1
val : その3

stl5関数の実行が終了しました


setコンテナ

std::setの使い方についてです。
setの中にクラスを格納する場合、以下の制約があります。
  ・<演算子をオーバーロードし、適切な結果を返すよう実装していること
  ・デフォルトコンストラクタを定義していること
setは内部で、ソートした状態で要素を格納しており、そのソートで<演算子を使うみたい。。findメンバ関数で特定の要素を取得しているが、これどうやって検索してんだって思ったら、これもやっぱり<演算子を使ってるみたい。

Stl6.cpp
#include <string>
#include <iostream>

#include <set>  // setを使用するのに必要

class Stl6 {
public:
        Stl6() { setMyName(""); }
        Stl6(std::string myName) { this->myName = myName; }

        bool operator < (const Stl6& left) const {
                return this->getMyName() < left.getMyName();
        }

        std::string getMyName() const { return myName; }
        void setMyName(std::string myName) { this->myName = myName; }
private:
        std::string myName;
};


void stl6() {
        std::set<Stl6> set;

        // ■setに要素の追加をする
        set.insert(Stl6("その1"));
        set.insert(Stl6("その2"));
        set.insert(Stl6("その3"));

        // ■setに含まれる要素をすべて表示する
        std::cout << "setに含まれる要素をすべて表示します\n";
        std::set<Stl6>::iterator setIterator = set.begin();
        while (setIterator != set.end()) {
                std::cout << "set : " << setIterator->getMyName() << "\n";
                setIterator++;  // 次の要素に進める
        }

        // ■setから特定の値を取得する
        std::cout << "setから特定の値を取得します\n";
        std::set<Stl6>::iterator findIterator = set.find(Stl6("その2"));
        if (findIterator != set.end()) {
                // 要素を発見
                std::cout << "findで取得したオブジェクト : " << findIterator->getMyName() << "\n";
        }

        std::cout << "\nstl6関数が終了しました\n";
}


stl6関数の実行結果
setに含まれる要素をすべて表示します
set : その1
set : その2
set : その3
setから特定の値を取得します
findで取得したオブジェクト : その2

stl6関数が終了しました


multisetコンテナ

std::multisetの使い方についです。
setがキーとそのキーに紐づく値が1個だったのに対し、こちらは複数紐づきます。

キーに紐づく値が複数という点以外、setとほぼ同じです(制約も)。

Stl7.cpp
#include <string>
#include <iostream>

#include <set>  // multisetを使用するのに必要


class Stl7 {
public:
        Stl7() { setMyName(""); }
        Stl7(std::string myName) { this->myName = myName; }

        bool operator < (const Stl7& left) const {
                return this->getMyName() < left.getMyName();
        }

        std::string getMyName() const { return myName; }
        void setMyName(std::string myName) { this->myName = myName; }
private:
        std::string myName;
};


void stl7() {

        std::multiset<Stl7> multiset;
        
        // ■multisetに要素を追加する
        multiset.insert(Stl7("その1"));
        multiset.insert(Stl7("その1"));
        multiset.insert(Stl7("その2"));

        // ■multisetの要素をすべて表示する
        std::cout << "すべての要素を表示します\n";
        std::multiset<Stl7>::iterator allIterator = multiset.begin();
        while (allIterator != multiset.end()) {
                std::cout << "要素 : " << allIterator->getMyName() << "\n";
                allIterator++;  // 次の要素に進める
        }

        // ■multisetから特定の要素を取得する
        std::cout << "multisetから特定の要素を取得します\n";
        std::multiset<Stl7>::iterator findIterator = multiset.find(Stl7("その1"));
        if (findIterator != multiset.end()) {
                do {
                        std::cout << "要素 : " << findIterator->getMyName() << "\n";
                } while (++findIterator != multiset.upper_bound(Stl7("その1")));
        }

        std::cout << "\nstl7関数が終了しました\n";
}


stl7関数の実行結果
すべての要素を表示します
要素 : その1
要素 : その1
要素 : その2
multisetから特定の要素を取得します
要素 : その1
要素 : その1

stl7関数が終了しました


queueコンテナ

コンテナアダプタである、std::queueの使い方についてです。
アダプタ・・・なんて名前がついていますが、コンテナアダプタ自体もコンテナです。他のコンテナと違うのは、内部で使用するデータ格納に使用するコンテナを外部から指定できる点・・・かな?(stl8_2の例を参照)。
カスタムコンテナなどを作って、それを指定し、効率を高めることができるかもしれな・・・らしいです。個人的には、ほとんどの場合は、ただのコンテナとしてしか使わない気がします。。。

stl8_2は、list型を内部で使用るように指定しています。最初のqueueを定義する部分がそれにあたります。処理自体はstl8_1と同じです。
queueの要求する仕様を満たしているコンテナを指定する必要があります。queueの場合listなどが該当します(vectorはNG。pop_frontメンバ関数がサポートされていないため)。

Stl8.cpp
#include <string>
#include <iostream>

#include <queue>        // queueを使用するのに必要
#include <list>         // listを使用するのに必要


void stl8_1() {
        std::queue<std::string> queue;

        // ■queueに要素を追加する
        queue.push("その1");
        queue.push("その2");
        queue.push("その3");

        // ■queueから要素を取得する
        while (!queue.empty()) {
                // frontで要素を取得し、popでその要素をqueueから取り除く(2段階の作業が必要)
                std::cout << "要素 : " << queue.front() << "\n";
                queue.pop();
        }

        std::cout << "\nstl8_1関数が終了しました\n";
}

void stl8_2() {
        std::queue<std::string, std::list<std::string> > queue;

        // ■queueに要素を追加する
        queue.push("その1");
        queue.push("その2");
        queue.push("その3");

        // ■queueから要素を取得する
        while (!queue.empty()) {
                // frontで要素を取得し、popでその要素をqueueから取り除く(2段階の作業が必要)
                std::cout << "要素 : " << queue.front() << "\n";
                queue.pop();
        }

        std::cout << "\nstl8_2関数が終了しました\n";

}


stl8_1関数の実行結果
要素 : その1
要素 : その2
要素 : その3

stl8_1関数が終了しました


stl8_2関数の実行結果
要素 : その1
要素 : その2
要素 : その3

stl8_2関数が終了しました


priority_queueコンテナ

std::priority_queueの使い方についてです。
名前の通り、優先度付きqueueです。
優先度の判定をするのに<演算子を使用するので、希望の並び順となるように<演算子をオーバーロードして処理を記述します。

Stl9.cpp
#include <string>
#include <iostream>

#include <queue>        // queueを使用するのに必要

class Stl9 {
public:
        Stl9() { 
                // デフォルト値を設定する
                setMyName("default");
                setPriority(9999999);
        }
        Stl9(std::string myName, int priority) {
                setMyName(myName);
                setPriority(priority);
        }

        bool operator<(const Stl9& stl9) const {
                return getPriority() > stl9.getPriority();
        }

        void setMyName(std::string myName) { this->myName = myName; }
        std::string getMyName() const { return myName; }
        void setPriority(int priority) { this->priority = priority; }
        int getPriority() const { return priority; }
private:
        int priority;
        std::string myName;
};

void stl9() {
        std::priority_queue<Stl9> queue;

        // ■queueに要素を追加する
        queue.push(Stl9("その3", 3));
        queue.push(Stl9("その2", 2));
        queue.push(Stl9("その1", 1));

        // ■queueの要素を取得する
        while (!queue.empty()) {
                std::cout << queue.top().getMyName() << "\n";
                queue.pop();
        }

        std::cout << "\nstl9関数が終了しました\n";
}


stl9関数の実行結果
その1
その2
その3

stl9関数が終了しました


stackコンテナ

std::stackの使い方についてです。
説明はないですw
単純にstackの機能を提供してくれるコンテナです。

Stl10.cpp
#include <string>
#include <iostream>

#include <stack>        // stackを使用するのに必要


void stl10() {
        std::stack<std::string> stack;

        // ■stackに要素の追加を行う
        stack.push("その1");
        stack.push("その2");
        stack.push("その3");

        // ■stackから要素を取得する
        while (!stack.empty()) {
                std::cout << "要素 : " << stack.top() << "\n";
                stack.pop();    // popを行わないとスタックから要素が消えません
        }

        std::cout << "\nstl10関数が終了しました\n";
}


stl10関数の実行結果
要素 : その3
要素 : その2
要素 : その1

stl10関数が終了しました


コピー(copy)

コンテナからコンテナへ要素をコピーするアルゴリズムです。
コピー先のサイズは、コピー元より大きくないとエラーとなります。なので、コピー元のサイズを調べて・・・という処理前にひと手間必要です。

Stl11.cpp
#include <string>
#include <iostream>
#include <vector>
#include <list>

#include <algorithm>    // アルゴリズムを使用するのに必要


void stl11() {
        std::vector<std::string> vector;

        vector.push_back("だらしねぇという 戒めの心");
        vector.push_back("歪みねぇという  賛美の心");
        vector.push_back("仕方ないという  許容の心");

        // ■copyのサンプル
        // (copyではコンテナ内のサイズ自動拡張が呼ばれないみたいなので事前にサイズを合わせておく)
        std::list<std::string> list(vector.size());
        copy(vector.begin(), vector.end(), list.begin());
        // copy結果を表示する
        std::cout << "copyの処理結果を表示\n";
        std::list<std::string>::iterator copyResultIterator = list.begin();
        while (copyResultIterator != list.end()) {
                std::cout << "listへコピーした結果 : " << *copyResultIterator << "\n";
                copyResultIterator++;
        }

        std::cout << "\nstl11が完了しました\n";
}


stl11関数の実行結果
copyの処理結果を表示
listへコピーした結果 : だらしねぇという 戒めの心
listへコピーした結果 : 歪みねぇという  賛美の心
listへコピーした結果 : 仕方ないという  許容の心

stl11が完了しました


検索(find/find_if)

コンテナから、指定された値を検索するアルゴリズムです。
stl12_1のほうには、コンテナが単純に数値がはいっているケース、stl12_2は、コンテナにクラスが入っているケースです。

Stl12.cpp
#include <string>
#include <iostream>
#include <vector>

#include <algorithm>    // アルゴリズムを使用するのに必要


void stl12_1() {
        std::vector<int> vector;

        vector.push_back(10);
        vector.push_back(20);
        vector.push_back(30);

        // ■vectorから20のものを探す
        std::vector<int>::iterator iterator = find(vector.begin(), vector.end(), 20);
        if (iterator != vector.end()) {
                std::cout << "発見しました! : " << *iterator << "\n";
        }

        std::cout << "\nstl12_1関数が終了しました\n";
}


// find_ifに渡す検索条件の関数
bool isKyoyou(std::string msg) {
        // 「許容」という文言がmsg内のどこに出現するかを調べる
        std::string::size_type index = msg.find("許容");
        if (index != std::string::npos) {
                // 該当する文字列を発見
                return true;
        } else {
                // 該当する文字列が発見できなかった
                return false;
        }
}

void stl12_2() {
        std::vector<std::string> vector;

        vector.push_back("だらしねぇという 戒めの心");
        vector.push_back("歪みねぇという  賛美の心");
        vector.push_back("仕方ないという  許容の心");

        // ■vectorから、「許容」という部分文字列に一致する要素を探す
        std::vector<std::string>::iterator iterator = find_if(vector.begin(), vector.end(), isKyoyou);
        if (iterator != vector.end()) {
                std::cout << "発見しました! : " << *iterator << "\n";
        }

        std::cout << "\nstl12_2関数が終了しました\n";
}


stl12_1関数の実行結果
発見しました! : 20

stl12_1関数が終了しました


stl12_2関数の実行結果
発見しました! : 仕方ないという  許容の心

stl12_2関数が終了しました


並び替え(sort)

コンテナ内の並び順をソートします。
sortを使用するには、前提条件があり、使用するコンテナはランダムアクセス反復子をサポートしていなければなりません(なので、std::listなどには使えません(std::listは自身のメンバ関数listを用いてソートを行う))。

Stl13.cpp
#include <string>
#include <iostream>
#include <vector>

#include <algorithm>    // アルゴリズムを使用するのに必要


void stl13_1() {
        std::vector<int> vector;

        vector.push_back(30);
        vector.push_back(20);
        vector.push_back(10);

        // ■ソートを行う
        std::sort(vector.begin(), vector.end());

        // ■結果を表示
        std::cout << "ソート結果を表示\n";
        std::vector<int>::iterator iterator = vector.begin();
        while (iterator != vector.end()) {
                std::cout << "要素 : " << *iterator << "\n";
                iterator++;     // 次の要素に進める
        }

        std::cout << "\nstl13関数が終了しました\n";
}

class Stl13 {
public:
        Stl13(std::string myName, int order) { 
                setMyName(myName);
                setOrder(order);
        }
        std::string getMyName() { return myName; }
        void setMyName(std::string myName) { this->myName = myName; }
        int getOrder() { return order; }
        void setOrder(int order) { this->order = order; }
private:
        std::string myName;
        int order;
};


// sortに渡す要素の並び順判定処理
bool sortStl13(Stl13& left, Stl13& rigth) {
        return left.getOrder() < rigth.getOrder();
}

void stl13_2() {
        std::vector<Stl13> vector;

        vector.push_back(Stl13("その3", 3));
        vector.push_back(Stl13("その2", 2));
        vector.push_back(Stl13("その1", 1));

        // ■ソートを行う
        std::sort(vector.begin(), vector.end(), sortStl13);

        // ■結果を表示
        std::cout << "ソート結果を表示\n";
        std::vector<Stl13>::iterator iterator = vector.begin();
        while (iterator != vector.end()) {
                std::cout << "要素 : " << iterator->getMyName() << "\n";
                iterator++;     // 次の要素に進める
        }

        std::cout << "\nstl13関数が終了しました\n";
}


stl13_1関数の実行結果
ソート結果を表示
要素 : 10
要素 : 20
要素 : 30

stl13関数が終了しました


stl13_2関数の実行結果
ソート結果を表示
要素 : その1
要素 : その2
要素 : その3

stl13関数が終了しました


二分検索(binary_search)

binary_seachは、コンテナに指定した値があるかないかのみを調べます。
実際にその要素の値がほしい場合は、別途lower_boundなどのアルゴリズムで取得する必要があります。

lower_boundは、指定した要素を発見したら、そのIteratorを返す。指定した要素が発見できなかったら、指定した要素の挿入位置を返します。
初見じゃ想像できないような仕様なので、わかってから使わないとハマります。。。

stl14_1では、コンテナに数値がはいっている場合の例です。stl14_2ではコンテナにクラスが入っている場合の例です。
stl14_2では、BinarySearchStl4という構造体を作成しまいた。これが、二分探索の際の現在位置より右か左かを判定する際の判定処理です。特にクラスにする必要がなかったので、構造体としています。
最初の1個だけでいいかなと思ったのですが、lower_boundは、そのあとにつづく2個のケースも呼ぶことがあるらしいので、念のため書いています。

Stl14.cpp
#include <string>
#include <iostream>
#include <vector>

#include <algorithm>    // アルゴリズムを使用するのに必要

void stl14_1() {

        std::vector<int> vector;

        vector.push_back(30);
        vector.push_back(20);
        vector.push_back(10);

        // ■二分探索はソート済みが前提なので、ソートしておく
        sort(vector.begin(), vector.end());

        // ■20という値がvector内に存在するか二分探索を行う(binary_searchは発見したか否かしか分からない)
        bool result = binary_search(vector.begin(), vector.end(), 20);
        if (result) {
                // ■20という値の要素を取得する
                std::vector<int>::iterator iterator = lower_bound(vector.begin(), vector.end(), 20);
                std::cout << "20の要素を取得しました! : " << *iterator << "\n";
        }

        std::cout << "\nstl14_1関数が終了しました\n";
}


class Stl14 {
public:
        Stl14(std::string myName, int order) { 
                setMyName(myName);
                setOrder(order);
        }
        Stl14(int order) { 
                setMyName("");
                setOrder(order);
        }
        Stl14(Stl14& obj) {
                setMyName(obj.getMyName());
                setOrder(obj.getOrder());
        }
        std::string getMyName() const { return myName; }
        void setMyName(std::string myName) { this->myName = myName; }
        int getOrder() const { return order; }
        void setOrder(int order) { this->order = order; }
private:
        std::string myName;
        int order;
};

bool sortStl14(const Stl14& left, const Stl14& right) {
        return left.getOrder() < right.getOrder();
}

struct BinarySearchStl4 {
        bool operator()(const Stl14& left, const Stl14& right)  {
                return left.getOrder() < right.getOrder();
        }
        bool operator()(const Stl14& left, int right)  {
                return left.getOrder() < right;
        }
        bool operator()(int left, const Stl14& right)  {
                return left < right.getOrder();
        }
};

void stl14_2() {
        std::vector<Stl14> vector;

        vector.push_back(Stl14("その5", 5));
        vector.push_back(Stl14("その4", 4));
        vector.push_back(Stl14("その3", 3));
        vector.push_back(Stl14("その2", 2));
        vector.push_back(Stl14("その1", 1));

        // ■二分探索はソート済みが前提なので、ソートしておく
        sort(vector.begin(), vector.end(), sortStl14);

        // ■order==3の探索対象があるかないかを判定する
        bool result = binary_search(vector.begin(), vector.end(), Stl14(3), BinarySearchStl4());
        if (result) {
                // ■order==3の要素を取得する
                std::vector<Stl14>::iterator iterator = lower_bound(vector.begin(), vector.end(), Stl14(3), BinarySearchStl4());
                std::cout << "要素 : " << iterator->getMyName() << "\n";
        }

        std::cout << "\nstl14_2関数が終了しました\n";
}


stl14_1関数の実行結果
20の要素を取得しました! : 20

stl14_1関数が終了しました


stl14_2関数の実行結果
要素 : その3

stl14_2関数が終了しました


transformについて(transform)

transformを使うと、2つのコンテナのデータを元に1つのメソッドを通し、新たなデータの生成を行います(stl15_1の例)。
1つのコンテナのデータを元に1つのメソッドを通し、新たなデータの生成も可能です(stl15_2の例)。

Stl15.cpp
#include <string>
#include <iostream>
#include <vector>

#include <algorithm>    // アルゴリズムを使用するのに必要


std::string transformStl15(std::string a, std::string b) {
        return a + " " + b ;
}

void stl15_1() {
        std::vector<std::string> vector1;
        vector1.push_back("だらしねぇという");
        vector1.push_back("歪みねぇという");
        vector1.push_back("仕方ないという");

        std::vector<std::string> vector2;
        vector2.push_back("戒めの心");
        vector2.push_back("賛美の心");
        vector2.push_back("許容の心");

        std::vector<std::string> vector3(vector1.size());

        // ■vector1とvector2を元に、vector3を作成する
        transform(vector1.begin(), vector1.end(), vector2.begin(), 
                vector3.begin(), transformStl15);

        std::vector<std::string>::iterator iterator = vector3.begin();
        while (iterator != vector3.end()) {
                std::cout << "要素 :" << *iterator << "\n";
                iterator++;     // 次の要素へ進める
        }

        std::cout << "\nstl15_1が終了しました\n";
}


std::string transformStl15Decorate(std::string a) {
        return "「" + a + "」";
}

void stl15_2() {
        std::vector<std::string> vector1;
        vector1.push_back("だらしねぇな");
        vector1.push_back("歪みねぇな");
        vector1.push_back("仕方ないね");

        std::vector<std::string> vector2(vector1.size());

        // ■vector1をtransformにかけvector2を作成する
        transform(vector1.begin(), vector1.end(), vector2.begin(), transformStl15Decorate);

        std::vector<std::string>::iterator iterator = vector2.begin();
        while (iterator != vector2.end()) {
                std::cout << "要素 :" << *iterator << "\n";
                iterator++;     // 次の要素へ進める
        }

        std::cout << "\nstl15_2が終了しました\n";
}


stl15_1関数の実行結果
要素 :だらしねぇという 戒めの心
要素 :歪みねぇという 賛美の心
要素 :仕方ないという 許容の心

stl15_1が終了しました


stl15_2関数の実行結果
要素 :「だらしねぇな」
要素 :「歪みねぇな」
要素 :「仕方ないね」

stl15_2が終了しました


並び順の変更(rotateとrandom_shuffle)

コンテナの並び順を、rotateは、ずらします。random_shuffleは並び順をランダムにします。

Stl16.cpp
#include <string>
#include <iostream>
#include <vector>

#include <algorithm>    // アルゴリズムを使用するのに必要


void stl16_1() {
        std::vector<int> vector1;
        vector1.push_back(1);
        vector1.push_back(2);
        vector1.push_back(3);
        vector1.push_back(4);
        vector1.push_back(5);

        // ■rotateを行う(左方向)
        std::cout << "rotateを行う(左方向)\n";
        rotate(vector1.begin(), vector1.begin() + 1, vector1.end());
        std::vector<int>::iterator iterator1 = vector1.begin();
        while (iterator1 != vector1.end()) {
                std::cout << "要素 : " << *iterator1 << "\n";
                iterator1++;    // 要素を次に進める
        }

        std::vector<int> vector2;
        vector2.push_back(1);
        vector2.push_back(2);
        vector2.push_back(3);
        vector2.push_back(4);
        vector2.push_back(5);

        // ■rotateを行う(右方向)
        std::cout << "rotateを行う(右方向)\n";
        rotate(vector2.rbegin(), vector2.rbegin() + 1, vector2.rend());

        std::vector<int>::iterator iterator2 = vector2.begin();
        while (iterator2 != vector2.end()) {
                std::cout << "要素 : " << *iterator2 << "\n";
                iterator2++;    // 要素を次に進める
        }

        std::cout << "\nstl16_1関数が終了しました\n";
}

void stl16_2() {
        std::vector<int> vector;
        vector.push_back(1);
        vector.push_back(2);
        vector.push_back(3);
        vector.push_back(4);
        vector.push_back(5);

        // ■shuffleを行う
        random_shuffle(vector.begin(), vector.end());

        std::vector<int>::iterator iterator = vector.begin();
        while (iterator != vector.end()) {
                std::cout << "要素 : " << *iterator << "\n";
                iterator++;     // 要素を次に進める
        }

        std::cout << "\nstl16_2関数が終了しました\n";
}


stl16_1関数の実行結果
rotateを行う(左方向)
要素 : 2
要素 : 3
要素 : 4
要素 : 5
要素 : 1
rotateを行う(右方向)
要素 : 5
要素 : 1
要素 : 2
要素 : 3
要素 : 4

stl16_1関数が終了しました


stl16_2関数の実行結果
要素 : 5
要素 : 2
要素 : 4
要素 : 3
要素 : 1

stl16_2関数が終了しました


マージ処理(merge)

二つのコンテナの要素をマージする処理です。両コンテナはソート済みである必要があります。

Stl17.cpp
#include <string>
#include <iostream>
#include <vector>

#include <algorithm>    // アルゴリズムを使用するのに必要


void stl17() {

        std::vector<int> vector1;
        vector1.push_back(1);
        vector1.push_back(2);
        vector1.push_back(3);

        std::vector<int> vector2;
        vector2.push_back(3);
        vector2.push_back(4);
        vector2.push_back(5);

        std::vector<int> vector3(vector1.size() + vector2.size());

        // ■マージ処理を行う(ソート済みコンテナをマージする)
        std::cout << "merge処理をする\n";
        merge(vector1.begin(), vector1.end(), vector2.begin(), vector2.end(), vector3.begin());

        std::vector<int>::iterator iterator1 = vector3.begin();
        while (iterator1 != vector3.end()) {
                std::cout << "要素 : " << *iterator1 << "\n";
                iterator1++;    // 要素を次に進める
        }

        std::cout << "stl17関数が終了しました\n";
}


stl17関数の実行結果
merge処理をする
要素 : 1
要素 : 2
要素 : 3
要素 : 3
要素 : 4
要素 : 5
stl17関数が終了しました


最大値・最少値の検索(max_element/min_element)

コンテナから最大値・最少値を取得する例です。

Stl18.cpp
#include <string>
#include <iostream>
#include <vector>

#include <algorithm>    // アルゴリズムを使用するのに必要


void stl18() {

        std::vector<int> vector;

        vector.push_back(10);
        vector.push_back(20);
        vector.push_back(30);

        // ■最大値を取得する
        std::vector<int>::iterator maxIterator = max_element(vector.begin(), vector.end());
        std::cout << "最大値 : " << *maxIterator << "\n";

        // ■最少値を取得する
        std::vector<int>::iterator minIterator = min_element(vector.begin(), vector.end());
        std::cout << "最小値 : " << *minIterator << "\n";

        std::cout << "stl18関数の処理終了\n";
}


stl18関数の実行結果
最大値 : 30
最小値 : 10
stl18関数の処理終了


for_eachアルゴリズム(for_each)

コンテナの各要素を引数に、for_eachの引数で指定した関数を呼び出します。
サンプルコードの例はあまりよくないですが、なにかいい感じに使えそうです。

Stl19.cpp
#include <string>
#include <iostream>
#include <vector>

#include <algorithm>    // アルゴリズムを使用するのに必要


void decorate(std::string msg) {
        std::cout << "「" + msg + "」" << "\n";
}

void stl19() {
        std::vector<std::string> vector;
        vector.push_back("だらしねぇという 戒めの心");
        vector.push_back("歪みねぇという  賛美の心");
        vector.push_back("仕方ないという  許容の心");

        // ■for_eachを使う
        for_each(vector.begin(), vector.end(), decorate);

        std::cout << "stl19関数の処理完了\n";
}


stl19関数の実行結果
「だらしねぇという 戒めの心」
「歪みねぇという  賛美の心」
「仕方ないという  許容の心」
stl19関数の処理完了


関数オブジェクト

関数オブジェクトは、operator()を実装したクラスのことです。引数は、関数オブジェクトを使うアルゴリズム次第です(単項目関数オブジェクトか、二項関数オブジェクトかのどちらか)。
通常、単項目関数オブジェクトは、unary_functionクラスを継承して作成します。二項関数オブジェクトはbinary_functionを継承して作成します。

以下の例で、単項目関数オブジェクトのクラスはunary_functionを継承しています。その中で、result_typeとarugument_typeが突然出てきていますが、これはunary_functionの中で定義されているものです。argument_typeは、unary_functionの第一型引数に該当します。そして、result_typeは、unary_functionの第二型引数に該当します。

また、二項関数オブジェクトはbinary_functionを継承しています。first_argument_typeは、binary_functionの第一型引数、second_argument_typeはbinary_functionの第二型引数に該当します。そして、result_typeはbinary_funtionの第三型引数に該当します。

アルゴリズムに渡すのは関数オブジェクトではなくても、関数をそのままポンといれても動作します。
それと比べ、関数オブジェクトを使う場合の良い点は以下のもの。
  ・関数オブジェクトを使えば、効率よく関数をアルゴリズムに渡すことができる(例:コンパイラは関数オブジェクトをインライン展開できる・・・など)
  ・関数オブジェクトはクラスなので、メンバ変数を追加したり処理を追加したりなにかと便利。

Stl20.cpp
#include <string>
#include <iostream>
#include <vector>

#include <functional>   // 関数オブジェクトを使用するのに必要
#include <algorithm>    // アルゴリズムを使用するのに必要


// 単項目関数オブジェクトの例
template<class T>
class Stl20Unary : public std::unary_function<T , bool> {
public:
        result_type operator()(argument_type i) {
                // 奇数ならtrueとなる
                if (i%2) {
                        return (result_type)true;
                } else {
                        return (result_type)false;
                }
        }
};

// 二項関数オブジェクトの例
template<class T>
class Stl20Binary : public std::binary_function<T, T, T> {
public:
        result_type operator()(first_argument_type a, second_argument_type b) {
                // aとbの値を加算する
                return (result_type)(a + b);
        }
};


void stl20() {
        std::vector<int> vector1;
        vector1.push_back(1);
        vector1.push_back(2);
        vector1.push_back(3);

        // count_ifに単項目関数オブジェクトを渡す
        int countResult = count_if(vector1.begin(), vector1.end(), Stl20Unary<int>());
        std::cout << "奇数の数(単項目関数オブジェクトを使った例) : " << countResult << "\n";

        // transformに関数オブジェクトを渡す(二項関数オブジェクトの例)
        std::vector<int> vector2;
        vector2.push_back(4);
        vector2.push_back(5);
        vector2.push_back(6);
        std::vector<int> vector3(vector1.size());

        // transformに二項関数オブジェクトを渡す
        std::cout << "transformに二項関数オブジェクトを使った例\n";
        transform(vector1.begin(), vector1.end(), vector2.begin(), vector3.begin(), Stl20Binary<int>());
        std::vector<int>::iterator iterator = vector3.begin();
        while (iterator != vector3.end()) {
                std::cout << "要素 : " << *iterator << "\n";
                iterator++;     // 要素を次に移動する
        }

        std::cout << "stl20関数の処理が終わりました\n";
}


stl20関数の実行結果
奇数の数(単項目関数オブジェクトを使った例) : 2
transformに二項関数オブジェクトを使った例
要素 : 5
要素 : 7
要素 : 9
stl20関数の処理が終わりました


バインダ

バインダは、二項関数オブジェクトに対し、どちらか一方を、指定の値で上書きして、処理を行わせます。
bind1stとbind2ndがあります。
bind1stは、左オペランドの値を第二引数の値に変更します。bind2ndは右オペランドの値を第二引数の値に変更します。

Stl21.cpp
#include <string>
#include <iostream>
#include <vector>

#include <functional>
#include <algorithm>


void stl21() {
        std::vector<int> vector;

        vector.push_back(1);
        vector.push_back(2);
        vector.push_back(3);
        vector.push_back(4);
        vector.push_back(5);

        int result;
        // bind1stを使っているため、「first > second」で比較をする処理を「2 > second」で置き換えている
        result = count_if(vector.begin(), vector.end(), std::bind1st(std::greater<int>(), 2));

        // bind2ndを使っているため、「first > second」で比較をする処理を「first > 2」で置き換えている
        std::cout << "std::bind1st(std::greater<int>(), 2)の結果 : " << result << "\n";
        result = count_if(vector.begin(), vector.end(), std::bind2nd(std::greater<int>(), 2));
        std::cout << "std::bind2nd(std::greater<int>(), 2)の結果 : " << result << "\n";

        std::cout << "stl21関数の処理が終わりました\n";
}


stl21関数の実行結果
std::bind1st(std::greater<int>(), 2)の結果 : 1
std::bind2nd(std::greater<int>(), 2)の結果 : 3
stl21関数の処理が終わりました


否定回路

述語に対するその処理結果の否定を返します。単項述語には、not1を、二項述語にはnot2を使用します。

Stl22.cpp
#include <string>
#include <iostream>
#include <vector>

#include <functional>
#include <algorithm>


void stl22() {

        std::vector<int> vector;
        vector.push_back(60);
        vector.push_back(50);
        vector.push_back(40);
        vector.push_back(30);
        vector.push_back(20);
        vector.push_back(10);

        // ■not1の使用例(バインダの実行結果は単項述語となるためnot1を使う)
        int result = count_if(vector.begin(), vector.end(), not1(bind2nd(std::less<int>(), 0)));
        std::cout << "count_ifの結果(not1の使用例) : " << result << "\n";

        // ■not2の使用例
        std::cout << "not2の使用例\n";
        sort(vector.begin(), vector.end(), std::not2(std::less<int>()));
        std::vector<int>::iterator iterator1 = vector.begin();
        while (iterator1 != vector.end()) {
                std::cout << "値 : " << *iterator1 << "\n";
                iterator1++;    // 次の要素へ移動する
        }

        std::cout << "\nstl22の処理が終了しました\n";
}


stl22関数の実行結果
count_ifの結果(not1の使用例) : 6
not2の使用例
値 : 60
値 : 50
値 : 40
値 : 30
値 : 20
値 : 10

stl22の処理が終了しました


関数アダプタ

STLのアルゴリズムは、関数を渡すこともできますが、基本的に関数アダプタで受け取るようになっています。
関数アダプタは、既存の関数をSTLのアルゴリズムで使用できるようにします。関数と、メンバ関数向けに関数アダプタが用意されています。その例を以下に示します。

関数のままだと、否定回路やバインダは使用できませんが、関数アダプタに変換することで、使用可能となります。

メンバ関数を関数アダプタに使用する場合、そのメンバ関数の戻り値の型はvoid型以外でなければならないらしいです。なので、今回のサンプルもそれに従っています。

Stl23.cpp
#include <string>
#include <iostream>
#include <vector>

#include <functional>
#include <algorithm>


// 二項述語
bool cmpStl23(int a, int b) {
        return a < b;
}

// 単項述語
bool judgeStl23(int a) {
        return a < 3;
}

class Stl23 {
public:
        Stl23() { setNum(0); }
        Stl23(int num) { setNum(num); }
        bool show() { std::cout << "num : " << getNum() << "\n"; return true; }
        int add(int add) { return num +=add; }

        int getNum() const { return num; }
        void setNum(int num) { this->num = num; }
private:
        int num;
};

void stl23() {
        std::vector<int> vector;
        vector.push_back(1);
        vector.push_back(2);
        vector.push_back(3);
        vector.push_back(4);
        vector.push_back(5);
        vector.push_back(6);

        // ■既存の関数を関数アダプタにいれてやれば、否定回路やバインダが使うことができる
        std::cout << "関数アダプタについて\n";
        sort(vector.begin(), vector.end(), not2(std::ptr_fun(&cmpStl23)));
        int count1 = count_if(vector.begin(), vector.end(), std::ptr_fun(&judgeStl23));
        int count2 = count_if(vector.begin(), vector.end(), bind2nd(std::ptr_fun(&cmpStl23), 3));

        std::vector<int>::iterator iterator = vector.begin();
        while (iterator != vector.end()) {
                std::cout << "要素 : " << *iterator << "\n";
                iterator++;     // 次の要素へ移動する
        }
        std::cout << "std::ptr_fun(&judgeStl23)の結果     : " << count1 << "\n";
        std::cout << "bind2nd(std::ptr_fun(&cmpStl23), 3) : " << count2 << "\n";


        // ■メンバ関数アダプタも有る
        std::cout<< "メンバ関数をメンバ関数アダプタを通して呼び出す\n";
        std::vector<Stl23*> vectorMem;
        vectorMem.push_back(new Stl23(1));
        vectorMem.push_back(new Stl23(2));
        vectorMem.push_back(new Stl23(3));

        for_each(vectorMem.begin(), vectorMem.end(), std::bind2nd(std::mem_fun(&Stl23::add), 10));
        for_each(vectorMem.begin(), vectorMem.end(), std::mem_fun(&Stl23::show));

        // 確保したメモリを解放
        std::vector<Stl23*>::iterator itMem = vectorMem.begin();
        while (itMem != vectorMem.end()) {
                delete *itMem;
                itMem++;        // 次の要素へ移動する
        }

        // ■参照を介してメンバ関数を呼び出す
        std::cout << "参照を介してメンバ関数を呼び出す場合\n";
        std::vector<Stl23> vectorMemRef;
        vectorMemRef.push_back(Stl23(1));
        vectorMemRef.push_back(Stl23(2));
        vectorMemRef.push_back(Stl23(3));

        for_each(vectorMemRef.begin(), vectorMemRef.end(), std::bind2nd(std::mem_fun_ref(&Stl23::add), 100));
        for_each(vectorMemRef.begin(), vectorMemRef.end(), std::mem_fun_ref(&Stl23::show));


        std::cout << "\nstl23関数の処理が完了しました\n";
}


stl23関数の実行結果
関数アダプタについて
要素 : 6
要素 : 5
要素 : 4
要素 : 3
要素 : 2
要素 : 1
std::ptr_fun(&judgeStl23)の結果     : 2
bind2nd(std::ptr_fun(&cmpStl23), 3) : 2
メンバ関数をメンバ関数アダプタを通して呼び出す
num : 11
num : 12
num : 13
参照を介してメンバ関数を呼び出す場合
num : 101
num : 102
num : 103

stl23関数の処理が完了しました