17

私はc++11の機能機能をいじっています。私が奇妙だと思うことの1つは、ラムダ関数の型が実際にはfunction<>型ではないということです。さらに、ラムダは型推論メカニズムとうまく連携していないようです。

添付されているのは、2つの整数を加算するために関数の2つの引数を反転することをテストした小さな例です。(私が使用したコンパイラは、MinGWではgcc 4.6.2でした。)この例では、の型はaddInt_ffunction <>を使用して明示的に定義されていますaddInt_lが、はラムダであり、その型は。で型推論されautoます。

コードをコンパイルすると、flip関数は明示的に型で定義されたバージョンのaddIntを受け入れることができますが、ラムダバージョンは受け入れることができず、次のようなエラーが発生します。 testCppBind.cpp:15:27: error: no matching function for call to 'flip(<lambda(int, int)>&)'

次の数行は、ラムダバージョン(および「raw」バージョン)が適切な関数<>型に明示的にキャストされている場合に受け入れることができることを示しています。

だから私の質問は:

  1. そもそもラムダ関数に型がないのはなぜfunction<>ですか?小さな例では、なぜ別のタイプではなくタイプとしてaddInt_l持っていないのですか?関数型プログラミングの観点から、関数/関数オブジェクトとラムダの違いは何ですか?function<int (int,int)>lambda

  2. これら2つが異なっていなければならないという根本的な理由がある場合。ラムダはに変換できると聞きましたfunction<>が、違います。これはC++11の設計上の問題/欠陥、実装上の問題ですか、それとも2つをそのまま区別することには利点がありますか?単独の型addInt_lアノテーションは、関数のパラメーターと戻り型に関する十分な情報を提供しているようです。

  3. 上記の明示的な型キャストを回避できるようにラムダを記述する方法はありますか?

前もって感謝します。

    //-- testCppBind.cpp --
    #include <functional>
    using namespace std;
    using namespace std::placeholders;

    template <typename T1,typename T2, typename T3>
    function<T3 (T2, T1)> flip(function<T3 (T1, T2)> f) { return bind(f,_2,_1);}

    function<int (int,int)> addInt_f = [](int a,int b) -> int { return a + b;};
    auto addInt_l = [](int a,int b) -> int { return a + b;};

    int addInt0(int a, int b) { return a+b;}

    int main() {
      auto ff = flip(addInt_f);   //ok
      auto ff1 = flip(addInt_l);  //not ok
      auto ff2 = flip((function<int (int,int)>)addInt_l); //ok
      auto ff3 = flip((function<int (int,int)>)addInt0);  //ok

      return 0;
    }

4

2 に答える 2

41

std::functionタイプに関係なく、あらゆる種類の呼び出し可能なオブジェクトを格納するのに役立つツールです。これを行うには、何らかの型消去技術を採用する必要があり、それにはいくらかのオーバーヘッドが伴います。

callable は暗黙的に に変換できるためstd::function、通常はシームレスに動作します。

明確にするために繰り返します。std::functionラムダや関数ポインターだけのものではなく、あらゆる種類の呼び出し可能なものです。これにはstruct some_callable { void operator()() {} };、たとえば のようなものが含まれます。これは単純なものですが、代わりに次のようにすることもできます。

struct some_polymorphic_callable {
    template <typename T>
    void operator()(T);
};

ラムダは、上記のオブジェクトのインスタンスに似た、もう 1 つの呼び出し可能なオブジェクトsome_callableです。呼び出し可能であるため a に格納できますstd::functionが、 の型消去オーバーヘッドはありませんstd::function

そして、委員会は将来、ラムダをポリモーフィックにすることを計画しています。つまり、some_polymorphic_callable上記のようなラムダです。std::functionそのようなラムダはどのタイプになりますか?


今... テンプレート パラメーター推定、または暗黙の変換。一つを選ぶ。それが C++ テンプレートのルールです。

ラムダをstd::function引数として渡すには、暗黙的に変換する必要があります。引数を取るというstd::functionことは、型推定よりも暗黙的な変換を選択していることを意味します。ただし、関数テンプレートでは、署名を推測または明示的に指定する必要があります。

ソリューション?発信者を に制限しないでくださいstd::functionあらゆる種類の callableを受け入れます。

template <typename Fun>
auto flip(Fun&& f) -> decltype(std::bind(std::forward<Fun>(f),_2,_1))
{ return std::bind(std::forward<Fun>(f),_2,_1); }

あなたは今、なぜそれが必要なのか考えているかもしれませんstd::function. std::function既知の署名を持つ callable の型消去を提供します。これにより、基本的に、型が消去された呼び出し可能オブジェクトを格納したり、virtualインターフェイスを記述したりするのに役立ちます。

于 2012-07-24T10:41:38.507 に答える
5
  1. 型消しfunction<>を採用しているため。これにより、いくつかの異なる関数のような型を に格納できますが、実行時にわずかなペナルティが発生します。型消去は、実際の型 (特定のラムダ) を仮想関数インターフェイスの背後に隠します。function<>
  2. これには利点があります。C++ 設計の「公理」の 1 つは、本当に必要でない限りオーバーヘッドを追加しないことです。autoこのセットアップを使用すると、型推論 (テンプレート パラメーターとして使用または渡す) を使用するときにオーバーヘッドは発生しませんが、function<>. また、function<>は言語構造ではなく、単純な言語機能を使用して実装できる標準ライブラリのコンポーネントであることに注意してください。
  3. function<>いいえ、ただし、 (ライブラリ構造)の詳細ではなく、関数の型 (言語構造) だけを取得する関数を作成できます。もちろん、実際に戻り値の型を書き留めるのは非常に難しくなります。パラメーターの型が直接得られるわけではないからです。ただし、 Boost.FunctionTypesのようなメタプログラミングを使用すると、渡す関数からこれらを推測できます。ただし、テンプレート化された を持つファンクターなど、これが不可能な場合もありますoperator()
于 2012-07-24T10:37:31.927 に答える