コンテナアダプタ
[第]38[課では]functionobjectについて学びました。
[今]、[私たちは]関数[アダプター]について学びます――[既存の]関数を[「改造」]して[必要な形に]するものです。
[まるでレゴブロックのように]、[小さなパーツを組み合わせて大きな作品を作り上げる]。
1. [アダプターの概要]
(1) 1.1 [アダプター]とは何か?
[アダプター](Adapter)は、[修正]functionobject[の動作]テンプレートです。
[一般的なアダプター]:
std::bind([バインドパラメータ])std::ref([注]参照渡し)std::negate([取り消し])std::mem_fn(メンバー関数ポインタ)
2. std::bind——[引数のバインド]
基本的な使い方
std::bind [関数のパラメータを[バインド]するために使用し、[新しい]関数オブジェクトを[作成]します。
例:[パラメータのバインド] (難易度 ⭐⭐)
▶ サンプル 2: codeexample (難易度 ⭐)
#include iostream
#include functional
int add(int a, int b) {
return a + b;
}
int main() {
// Bind first parameter of add to 10
auto add10 = std::bind(add, 10, std::placeholders::_1);
std::cout << "add10(5) = " << add10(5) << std::endl; // output:15
std::cout << "add10(20) = " << add10(20) << std::endl; // output:30
return 0;
}
💡 ヒント:
std::placeholders::_1[表示]「[最初の引数は後で入力してください]」
(2) 2.2 [パラメータの順序の調整]
例:[引数の順序を入れ替える] (難易度 ⭐⭐)
#include iostream
#include functional
int subtract(int a, int b) {
return a - b;
}
int main() {
// Swap parameter order
auto reverse_subtract = std::bind(subtract,
std::placeholders::_2,
std::placeholders::_1);
std::cout << "subtract(10, 3) = " << subtract(10, 3) << std::endl; // 7
std::cout << "reverse(10, 3) = " << reverse_subtract(10, 3) << std::endl; // -7
return 0;
}
3. std::ref——参照[ラップ]
(1) 3.1 [問題]:値渡し
[デフォルトでは]、STLアルゴリズムは[値渡し]の関数オブジェクトとして動作するため、[状態を共有することができません]。
例:[用]std::ref[解決] (難易度 ⭐⭐)
#include iostream
#include algorithm
#include vector
#include functional
struct Counter {
int count = 0;
void operator()(int) { count++; }
};
int main() {
std::vectorint v = {1, 2, 3, 4, 5};
Counter counter;
// ❌!pass by value,counter'scalling
std::for_each(v.begin(), v.end(), counter);
std::cout << ":" << counter.count << std::endl; // output:0
// ✅!Usingstd::refbyreference
std::for_each(v.begin(), v.end(), std::ref(counter));
std::cout << ":" << counter.count << std::endl; // output:5
return 0;
}
4. std::not_fn——[反転]
基本的な使い方
std::not_fn [functionobjectの戻り値を反転させるために使用します]。
例:[反転述語] (難易度 ⭐⭐)
#include iostream
#include vector
#include algorithm
#include functional
int main() {
std::vectorint v = {1, 2, 3, 4, 5};
// Find first even number
auto it1 = std::find_if(v.begin(), v.end(),
(int x) { return x % 2 == 0; });
std::cout << ":" << *it1 << std::endl; // 2
// Find first odd number (negated)
auto it2 = std::find_if(v.begin(), v.end(),
std::not_fn((int x) { return x % 2 == 0; }));
std::cout << ":" << *it2 << std::endl; // 1
return 0;
}
5. std::mem_fn——メンバ関数ポインタ
(1) 5.1 [問題]:メンバー関数ポインタ[使いにくい]
メンバー関数ポインタ[構文が複雑]、std::mem_fn [を使用すれば] [簡略化できる]。
例:[呼び出し]メンバー関数 (難易度 ⭐⭐⭐)
#include iostream
#include vector
#include algorithm
#include memory
struct Student {
std::string name;
void display() const {
std::cout << "Student: " << name << std::endl;
}
};
int main() {
std::vectorStudent students = {{"..."}, {"..."}};
// std::mem_fn
std::for_each(students.begin(), students.end(),
std::mem_fn(&Student::display));
return 0;
}
6. [総合]例
▶ サンプル 1: [柔軟な成績処理機能] (難易度 ⭐⭐⭐)
#include iostream
#include vector
#include algorithm
#include functional
int main() {
std::vectorint scores = {85, 92, 78, 90, 88};
int threshold = 90;
// Count scores not below threshold
int count = std::count_if(scores.begin(), scores.end(),
std::bind(std::greater_equalint(),
std::placeholders::_1,
threshold));
std::cout << "not below" << threshold << "people scoring:" << count << std::endl;
return 0;
}
❓ よくある質問
Q:C++11には[まだ]std::bind[はありますか]? A:[あります]が、[ただし]ラムダ式[の方が推奨されます]。ラムダ式[の方が簡潔]で、パフォーマンスも[優れています]。
// std::bind
auto f1 = std::bind(add, 10, std::placeholders::_1);
// Lambda()
auto f2 = (int x) { return add(10, x); };
Q:[いつアダプターを使うのか]? A:[既存の]関数が[ほぼ][要件を満たしている]が、[引数が一致しない場合]。
Q:STL adaptersについて最も重要なことは何ですか? A:まず核心概念を理解し、その後実践的な例で練習することが重要です。
📖 まとめ
| [アダプター] | [機能] |
|---|---|
std::bind |
[パラメータの紐付け] |
std::ref |
[按]参照渡し |
std::not_fn |
[取り消し] |
std::mem_fn |
メンバー関数ポインタ |
📝 練習問題
-
**初心者(難易度 ⭐):[stackint を作成し]、[順番に] 1、2、3 を push し、[その後] ループで pop を行い、[すべての要素を] 出力する。[出力される] 順序を[確認する]。
-
**中級(難易度 ⭐⭐):[用] queue [「印刷タスクのキュー」を実装する]――[複数の印刷タスクが順番に処理される様子をシミュレートし]、[1つずつ処理した後]、[キューの残りの長さ]を出力する。
-
**上級(難易度 ⭐⭐⭐):[用] priority_queue [「タスクスケジューラ」を実装する]――[各タスクには優先度](1~10)が設定されており、[キューは優先度の高い順に処理され]、[同じ優先度のタスクは挿入順に処理される]。
- [アダプタ]:stack/queue/priority_queue のカプセル化[基盤]コンテナ
- スタック [後入れ先出し]:push/pop/top
- キュー [先入れ先出し]:push/pop/front/back
- priority_queue [優先度キュー]:[最大]ヒープ
- [アダプタは]templateパラメータ[を通じて、基盤となる]コンテナを指定する
[次のレッスン]:例外処理(#40)



