std::function
は不完全だからです。
それはどのように不完全ですか?方法を列挙してみましょう。
{
まず、std::function
渡された任意のオブジェクトの完全な転送をサポートしていません。そして、実際には、それはできません。 std::function
1 つの固定署名を呼び出し元に公開し、さまざまな種類の呼び出し先を受け入れることができます。完全な転送には、呼び出し元と呼び出し先ごとにカスタム作成された署名が必要です。署名で公開する引数の完全な転送をサポートしますが、それだけでは不十分です。
std::function
2 つの引数を取るa とint
、a を想像してみてくださいdouble
。それが完全な転送を行うためには、同じセットを受け入れる必要がありますint&
(揮発int&&
性のバリアントは言うまでもありません)。完全な転送を実現するためにそれぞれが受け入れる必要が ある署名の数は、引数の数に応じて指数関数的に増加します。が公開する署名のセット (現在 1 つ) はインスタンス化時に固定されますが、公開する署名のテンプレート セットは無制限であり、使用時にのみ生成されます。一部の関数のようなオブジェクトは、これらのケースに対して異なる方法で最適化するため、これは重要です! そのため、ラップされた型への呼び出しを完全に転送する機会をすべて削除しました。int const&
double
std::function
std::function
std::function
が不完全である2 つ目の理由std::function
は、コンパイラがダメだからです。ラムダを でラップし、std::function
それを使用してアルゴリズムを呼び出すと、コンパイラは理論上、これstd::function
が固定ラムダをラップしていることを認識できますが、実際には、この事実を見失い、 をstd::function
ジェネリック クラスのラッパーとして扱います。 . したがって、 の署名がstd::function
アルゴリズムのユースケースと正確に一致し、 の型のボトルネックがstd::function
転送を不完全にするのを防ぐ場合でも、実際には によって実行される型の消去によるオーバーヘッドが発生しstd::function
、コンパイラはstd::function
コール「バリア」を最適化するのは難しいと思います。
が不完全である3 つ目の理由std::function
は、アルゴリズムの作成者がアルゴリズムで渡すことができるパラメーターを過度に制限することを奨励することです。を調べるfind_if
と、探しているものはコンテナーに格納されている型と同じ型であるか、少なくとも変換可能である必要があるという単純な仮定がありますが、std::find_if
アルゴリズムは、渡されたファンクターの下でそれらが比較可能であることのみを必要とします。
これにより、複数の型を認識するファンクターを作成し、コンテナーの型とは無関係な型のターゲット オブジェクトを渡すことができ、問題なく動作します。ほとんどの人はこれを必要とせず、彼らのコードはそれがなくても機能します - これも良いことです。
素朴なstd::find_if
人はコンテナの基礎となるタイプを抽出し、比較関数はそのタイプのペア間で行われます-または、コンテナタイプと検索対象のタイプとの間の4方向の比較になります。ある場合には、柔軟性が失われます。別の場合には、奇妙なコーナー ケースに誰もがお金を払います。そして C++ では、必要なときに必要な機能に対してのみ料金を支払う必要があります。
が不完全である 4 つ目の理由std::function
は、基本的に型消去のツールであることです。これらは実装の詳細ですが、それらからかけ離れたコンパイラを私は知りません。Astd::function
の目的は、単一の署名と戻り値を公開し、「この署名とこの戻り値に一致するものは何でも格納でき、呼び出すことができる」ということです。このタスクを実行するために、静的ランタイム インターフェイスと実装を公開します。を初期化するstd::function
と、コンパイル時の作業が行われ、その特定のオブジェクトを統一std::function
インターフェイスでラップするヘルパー オブジェクトが生成され、それがパターンに格納されpImpl
ます。これらはすべて、型消去が必要ない場合は不要な作業です。
標準アルゴリズムは、手作りのソリューションとほぼ同じくらい効率的な高レベルのコードを記述することに関するものです。これらの問題のほとんどを解決するために、ポインター関数呼び出しのコストでさえ必要ありませんstd::function
。
}; // enum
std::function
virtual
コールバック、定型的な単一目的インターフェイスの置き換え、および呼び出し元から実装の詳細を隠す必要がある場合 (たとえば、設計上の決定のためにコンパイル ユニットの境界を越える必要がある場合)のための優れたツールです。
良いニュースは、この問題に対するより良い解決策が次々と出てきていることです。特に、C++14 または C++17 の目標の 1 つは、「このテンプレート引数には次のプロパティがある」と言うことができる、ある種の「概念」をサポートすることです。正確な構文がどうなるかは、はっきりとは言えません.C++11 コンセプトの提案はおそらくかなり進んでいます.しかし、それについて多くの熱意があり、現在、この問題に関するワーキンググループがあります.
それが完了すると、「この引数は単なる任意の型ではなく、2 つの値 (含まれるデータ型を含む) を取るファンクターである型である」という意味のある概念情報でファンクターをマークアップできるようになります。 )、bool
関数のドキュメントにアクセスしなくても、コンパイラ、IDE、およびユーザーが理解できる互換性のある値を返します。