関数オブジェクトとラムダ式
[第]37[課では]iteratorについて学びました。
[今]、[私たちは]STLアルゴリズムの「[真髄]」――functionobject――を学びます。
アルゴリズム[は骨組み]であり、関数オブジェクト[は肉と血]である。[この両者が組み合わさって]初めて、STL[の真の威力を発揮]することができる。
1. functionobject[概要]
(1) 1.1 functionobjectとは何か?
関数オブジェクト(ファンクタ)[とは][関数][のように呼び出すことができる]オブジェクトのことである。
[3種類の]functionobject:
- 関数ポインタ
- functionobjectclass(オーバーロード
operator()) - Lambda[式](C++11)
(2) 1.2 なぜ functionobject が必要なのか?
STLアルゴリズムは[はい][汎用的な]ものですが、[具体的な操作][は要件によって異なります]。functionobjectを使えば、[操作をカスタマイズ]することができます。
[生活]クラス[比]:
- アルゴリズム = [洗濯機]([汎用])
- functionobject = [洗濯用液体洗剤]([カスタム]:[爽やかな香りタイプ]/[強力タイプ]/[しなやかタイプ])
2. 関数ポインタ
基本的な使い方
例:[用]関数ポインタ[カスタムソート] (難易度 ⭐⭐)
▶ サンプル 2: STLコンテナ[使用] (難易度 ⭐)
#include iostream
#include vector
#include algorithm
// ...
bool compareDesc(int a, int b) {
return a > b; // ...
}
int main() {
std::vectorint v = {3, 1, 4, 1, 5, 9, 2, 6};
// ...
std::sort(v.begin(), v.end(), compareDesc);
for (int x : v) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
[実行結果]:
9 6 5 4 3 2 2 1 1
💡 ヒント:
- 関数ポインタ[は]C[言語の遺産]であり、C++では[より推奨される]関数オブジェクト[または]ラムダ式
3. 関数・オブジェクト・クラス
(1) 3.1 functionobjectclass とは何か?
functionobjectclass[は]オーバーロードされたoperator()[の]classであり、[その]インスタンスは[関数][のように呼び出すことができる]。
例:[カスタムコンパレータ] (難易度 ⭐⭐)
#include iostream
#include vector
#include algorithm
#include string
//:
struct CompareByLength {
bool operator()(const std::string& a, const std::string& b) const {
return a.length() < b.length();
}
};
int main() {
std::vectorstd::string words = {"apple", "banana", "cat", "dog"};
// ...
std::sort(words.begin(), words.end(), CompareByLength());
for (const auto& w : words) {
std::cout << w << " ";
}
std::cout << std::endl;
return 0;
}
[実行結果]:
cat dog apple banana
(2) 3.2 functionobject[の利点]
| [比較] | 関数ポインタ | 関数オブジェクトクラス |
|---|---|---|
| [状態] | [状態なし] | [状態あり](メンバー変数) |
| パフォーマンス | [インライン化できない可能性がある] | [インライン化可能]、[高速] |
| [柔軟性] | [低] | [高]([テンプレート化]可能) |
(3) 3.3 [状態を持つ]functionobject
例:[カウンター]functionobject (難易度 ⭐⭐⭐)
#include iostream
#include algorithm
#include vector
//:
struct Counter {
int threshold; // ()
Counter(int t) : threshold(t) {}
bool operator()(int x) const {
return x > threshold; // threshold
}
};
int main() {
std::vectorint v = {1, 5, 10, 15, 20};
//,10
Counter counter(10);
// 10
int count = std::count_if(v.begin(), v.end(), counter);
std::cout << "10:" << count << std::endl; // output:2
return 0;
}
4. Lambda[式]
(1) 4.1 ラムダとは何か?
Lambda[式]は、C++11で導入された[匿名]関数であり、関数が必要な場所で直接定義することができます。
[基本構文]:
[capture](parameters) -> return_type { body }
| [一部] | [説明] |
|---|---|
capture |
[キャプチャリスト]([外部キャプチャ]変数) |
parameters |
[パラメータ一覧] |
return_type |
[戻る]タイプ([省略可]) |
body |
関数本体 |
(2) 4.2 [基本]例
例:Lambda[ソート] (難易度 ⭐)
#include iostream
#include vector
#include algorithm
int main() {
std::vectorint v = {3, 1, 4, 1, 5, 9, 2, 6};
// Lambda()
std::sort(v.begin(), v.end(), (int a, int b) {
return a > b;
});
for (int x : v) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
(3) 4.3 [キャプチャリスト]
[キャプチャリスト][決定]Lambda[がアクセスできる外部]変数。
| [取得方法] | [説明] |
|---|---|
| `` | [変数を一切取得しない] |
[x] |
[按]value[捕獲]x |
[&x] |
[参照] [キャプチャ] x |
[=] |
[按]value[すべての変数を取得] |
[&] |
[参照] [すべて取得] 変数 |
[this] |
[キャプチャ]thispointer([クラス内での]使用) |
例:[状態を持つ]Lambda (難易度 ⭐⭐)
#include iostream
#include vector
#include algorithm
int main() {
std::vectorint v = {1, 5, 10, 15, 20};
int threshold = 10;
// threshold
int count = std::count_if(v.begin(), v.end(),
[threshold](int x) {
return x > threshold;
});
std::cout << "..." << threshold << ":" << count << std::endl;
return 0;
}
5. STL[事前定義]functionobject
(1) 5.1 [算術]関数オブジェクト
functional ヘッダーファイルには、[よく使われる]関数オブジェクトが提供されています:
| 関数オブジェクト | [機能] | 例 |
|---|---|---|
std::plusT |
[加法] | std::plusint() |
std::minusT |
[減法] | std::minusint() |
std::multipliesT |
[乗法] | std::multipliesint() |
std::dividesT |
[除法] | std::dividesint() |
std::negateT |
[負の値] | std::negateint() |
例:[用]multiplies[倍増] (難易度 ⭐)
#include iostream
#include vector
#include algorithm
#include functional
int main() {
std::vectorint v = {1, 2, 3, 4, 5};
// ...
std::transform(v.begin(), v.end(), v.begin(),
std::bind(std::multipliesint(), std::placeholders::_1, 2));
for (int x : v) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
(2) 5.2 [比較]functionobject
| 関数オブジェクト | [機能] |
|---|---|
std::equal_toT |
[等于] |
std::not_equal_toT |
[等しくない] |
std::greaterT |
[大于] |
std::lessT |
[小于] |
std::greater_equalT |
[以上] |
std::less_equalT |
[以下] |
(3) 5.3 [論理]functionobject
| 関数オブジェクト | [機能] |
|---|---|
std::logical_andT |
[論理積] |
std::logical_orT |
[論理和] |
std::logical_notT |
[論理否定] |
6. [総合]例
▶ サンプル 1: [成績処理プログラム] (難易度 ⭐⭐⭐)
#include iostream
#include vector
#include algorithm
#include functional
struct Student {
std::string name;
int score;
};
int main() {
std::vectorStudent students = {
{"...", 85},
{"...", 92},
{"...", 78}
};
// 1. bysorting
std::sort(students.begin(), students.end(),
(const Student& a, const Student& b) {
return a.score > b.score;
});
// 2.
auto max_it = std::max_element(students.begin(), students.end(),
(const Student& a, const Student& b) {
return a.score < b.score;
});
std::cout << "points:" << max_it->name << " " << max_it->score << std::endl;
// 3.
int passed = std::count_if(students.begin(), students.end(),
(const Student& s) {
return s.score >= 60;
});
std::cout << ":" << passed << std::endl;
return 0;
}
❓ よくある質問
Q:ラムダ式と関数オブジェクト・クラス、どちらが良いですか? A:- [単純な処理] → ラムダ式(コードが簡潔) - [複雑な処理]/[再利用が必要な場合] → 関数オブジェクト・クラス(保守性が高い)
Q:
auto[ラムダ型]を[導出]できますか? A:ラムダ型は[唯一の匿名]型であり、autoによる[導出]に[限定]され、[具体的な]型を[記述]することはできません。
auto func = (int x) { return x * 2; };
// std::function<int(int)> func = ... //,
Q:[いつ使うのか]
std::function? A:[functionobjectを][保存]する必要がある場合([メンバー変数として]、[戻り値として]など)、std::functionを使います。
📖 まとめ
| [知識ポイント] | [要点] |
|---|---|
| 関数ポインタ | [シンプルだが機能は限定的] |
| 関数・オブジェクト・クラス | [カスタマイズ可能な操作],[状態を持つことができる] |
| Lambda | [匿名]関数,[簡潔かつ強力] |
| [事前定義]関数オブジェクト | std::plus[など],functional[内] |
| [キャプチャリスト] | Lambda[外部変数へのアクセス方法] |
📝 練習問題
-
**初心者(難易度 ⭐):[function(operator() をオーバーロードしたクラス)を模倣して作成し]、[「2つの整数の大きさを比較する」機能を実装し]、[std::sort を使ってテストする]。
-
**中級(難易度 ⭐⭐):[std::function を使用して] 異なる型の呼び出し可能なオブジェクト([通常の]関数、ラムダ式、[擬似]関数)を[一元的に呼び出す]。
-
**上級(難易度 ⭐⭐⭐):[std::bind を使用して] [一部の引数をバインドし]、[新しい呼び出し可能なオブジェクトを生成する]。[「引数の事前設定」] を行う [関数アダプタ] を実装する。
- functionobject([擬似]関数):operator() をオーバーロードする [クラス]
- 関数オブジェクト[状態の保存が可能]、[通常の]関数[不可]
- std::function:type[削除された呼び出し可能ラッパー]
- bind [一部のパラメータをバインドして新しい呼び出し可能なオブジェクトを生成]object
- ラムダ [は] 関数オブジェクト [の構文糖]
[次のレッスン]:STL[アダプタ](#39)



