ラムダを使用するよりもファンクタを作成する方が理にかなっている状況はありますか?
私の質問は事実上 ファンクタでラムダを使用する場合 の逆であることを知っていますが、実際にはファンクタがになる状況を考えることはできませんラムダよりも好ましい。これについて何か考えはありますか?
ラムダはファンクターです-短い構文で定義されているだけです。
問題は、この構文が制限されていることです。常に最も効率的で柔軟な方法で問題を解決できるとは限りません。 C++ 14までは、operator()
をテンプレートにすることさえできませんでした。
さらに、ラムダには正確に1つのoperator()
があります。たとえば、引数の型を区別するためにいくつかのオーバーロードを提供することはできません。
struct MyComparator
{
bool operator()( int a, int b ) const {return a < b;}
bool operator()( float a, float b ) const {return /*Some maths here*/;}
};
..またはオブジェクト引数の値カテゴリ(つまり、呼び出されるクロージャオブジェクト)。コンストラクタやデストラクタを含む特別なメンバー関数を定義することもできません-ファンクタがリソースを担当する場合はどうなりますか?
ラムダのもう1つの問題は、それらが再帰的にできないことです。もちろん、通常の機能(オペレーター機能を含む)も可能です。
また、ラムダは、連想コンテナのコンパレーターやスマートポインターの削除子として使用するには便利ではないことも考慮してください。テンプレートの引数としてクロージャータイプを直接渡すことはできず、別のクロージャーオブジェクトからコンテナーメンバーを構築する必要があります。 (クロージャ型にはデフォルトのコンストラクタがありません!)。それほど面倒ではないブロックスコープmap
の場合:
auto l = [val] (int a, int b) {return val*a < b;};
std::map<int, int, decltype(l)> map(l);
では、map
がデータメンバーの場合はどうなりますか?テンプレート引数、コンストラクタ初期化リストの初期化子は何ですか?別の静的データメンバーを使用する必要がありますが、クラス定義の外で定義する必要があるため、間違いなく醜いです。
まとめると:ラムダはそれらのために作成されていないため、より複雑なシナリオには役立ちません。これらは、対応する単純な状況でsimple関数オブジェクトを作成する短くて簡潔な方法を提供します。
私がラムダを超えるときにファンクタを使用することを検討します
次の2つのケースが考えられます。
ファンクタが内部状態を運ぶとき、ファンクタには重要な寿命問題があります。それは用途の間に「何か」を保ちます
至る所で同じコードを使用する必要がある場合は、それを独自のヘッダーにファンクターとして記述して保持することは、保守の観点からは良い考えです。
ラムダを 評価されていないコンテキストで使用 にすることはできません。特に不自然な例は Shafik's から盗まれます。
多くの場合、各ラムダは一意の型を持っているため、役に立たないだけです。架空の例を次に示します。
template<typename T, typename U> void g(T, U, decltype([](T x, T y) { return x + y; }) func); g(1, 2, [](int x, int y) { return x + y; });
宣言と呼び出しのラムダのタイプは異なります(定義により)、したがってこれは機能しません。
したがって、同じ構文のラムダであっても、別のラムダの代わりに使用することはできません。この場合、ファンクタが機能します。