関数オブジェクトとラムダ式
CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 74/104。
多くのSTLアルゴリズムは、その動作をカスタマイズするコーラブルオブジェクトを受け取ります。すでにstd::transformでラムダ式が使用されているのを見ました。C++でコーラブルオブジェクトを作成する2つの方法である、ファンクタとラムダ式の両方について詳しく見ていきましょう。
ファンクタ(関数オブジェクト)は、operator()をオーバーロードするクラスであり、インスタンスを関数のように呼び出すことができます。
#include <algorithm>
#include <vector>
#include <iostream>
struct MultiplyBy {
int factor;
MultiplyBy(int f) : factor(f) {}
int operator()(int x) const {
return x * factor;
}
};
int main() {
std::vector<int> nums = {1, 2, 3, 4};
std::vector<int> result(nums.size());
std::transform(nums.begin(), nums.end(), result.begin(), MultiplyBy(3));
// result: {3, 6, 9, 12}
}各ファンクタのインスタンスは、構築時に設定されたメンバ変数を通じて、独自の独立した状態を保持することができます。これは通常の関数呼び出しでは不可能なことです。しかし、単純な操作のためにクラスを定義するのは冗長です。ラムダ式は、簡潔な代替手段を提供します:
int factor = 3;
std::transform(nums.begin(), nums.end(), result.begin(),
[factor](int x) { return x * factor; });ラムダ式の構文は [capture](parameters) { body } です。キャプチャ節は、ラムダがアクセスできる外部変数を指定します。すべてを値でキャプチャするには [=] を、すべてを参照でキャプチャするには [&] を使用するか、[factor] や [&factor] のように特定の変数をリストします。
ラムダは、カスタム基準によるソートなどのアルゴリズムを使用した、一度限りの操作に特に便利です。
std::vector<int> nums = {5, -2, 8, -1};
std::sort(nums.begin(), nums.end(),
[](int a, int b) { return std::abs(a) < std::abs(b); });
// 絶対値順にソート: {-1, -2, 5, 8}チャレンジ
簡単商品の価格にさまざまな割引戦略を適用するために、関数オブジェクト(ファンクタ)とラムダ式の両方を活用する価格計算機を作成しましょう。
コードは以下の2つのファイルに分けて構成します:
Discounts.h: 割引ファンクタとユーティリティ関数をここで定義します。割引率(整数)を保持する
PercentageDiscountというファンクタを作成してください。そのoperator()はdouble型の価格を受け取り、割引後の価格を返す必要があります。例えば、$100に対して20%の割引を適用すると、$80を返します。次に、差し引く固定額(
double型)を保持するFixedDiscountというファンクタを作成してください。そのoperator()は価格を受け取り、価格から固定額を引いた値を返す必要があります(ただし、0未満にはなりません)。const std::vector<double>&を受け取り、すべての価格をスペースで区切って表示し、最後に改行を出力するprintPricesという関数を作成してください。各価格は小数点以下2桁でフォーマットしてください。main.cpp: 5つの入力(それぞれ別々の行)を読み取ります:- 1つ目の商品の価格 (double)
- 2つ目の商品の価格 (double)
- 3つ目の商品の価格 (double)
- 適用する割引率 (整数、例:20%の場合は 20)
- 固定割引額 (double)
3つの価格を含むベクトルを作成し、両方のアプローチを実演してください:
Original prices:に続いて価格を表示しますstd::transformとPercentageDiscountファンクタを使用して、割引後の価格の新しいベクトルを作成します。After percentage discount:に続いて結果を表示します- 元の価格に対して
std::transformとFixedDiscountファンクタを使用して、別のベクトルを作成します。After fixed discount:に続いて結果を表示します - 元の各価格を2倍にするラムダ式を使用して
std::transformを実行します。Premium prices (doubled):に続いて結果を表示します - ラムダ式を使用して
std::sortを実行し、元の価格を降順にソートします。Sorted (high to low):に続いてソートされた価格を表示します
例えば、入力が 100.00, 50.00, 75.00, 20, 15.00 の場合、出力は以下のようになります:
Original prices: 100.00 50.00 75.00
After percentage discount: 80.00 40.00 60.00
After fixed discount: 85.00 35.00 60.00
Premium prices (doubled): 200.00 100.00 150.00
Sorted (high to low): 100.00 75.00 50.00 このチャレンジでは、状態(割引額など)を保持するファンクタと、変数をキャプチャして迅速なインライン操作を行うラムダを比較できます。どちらのアプローチも、std::transform や std::sort などのSTLアルゴリズムとシームレスに連携します。
自分で試してみよう
#include <iostream>
#include <vector>
#include <algorithm>
#include "Discounts.h"
int main() {
// 入力を読み込む
double price1, price2, price3;
int percentageDiscount;
double fixedDiscount;
std::cin >> price1;
std::cin >> price2;
std::cin >> price3;
std::cin >> percentageDiscount;
std::cin >> fixedDiscount;
// 3つの価格を含むベクトルを作成する
std::vector<double> prices = {price1, price2, price3};
// TODO: printPricesを使用して、"Original prices:"に続いて価格を表示する
// TODO: PercentageDiscountファンクタを使用してstd::transformを使用する
// 結果用の新しいベクトルを作成する
// "After percentage discount:"に続いて結果を表示する
// TODO: 元の価格に対してFixedDiscountファンクタを使用してstd::transformを使用する
// 結果用の新しいベクトルを作成する
// "After fixed discount:"に続いて結果を表示する
// TODO: 各元の価格を2倍にするラムダ式を使用してstd::transformを使用する
// 結果用の新しいベクトルを作成する
// "Premium prices (doubled):"に続いて結果を表示する
// TODO: ラムダ式を使用して元の価格を降順にソートするためにstd::sortを使用する
// "Sorted (high to low):"に続いてソートされた価格を表示する
return 0;
}このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタ引数付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ詳解Rule of Three / Five / Zeroまとめ - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () 演算子のオーバーロード型変換演算子まとめ:Matrix クラス