web-dev-qa-db-ja.com

ラムダクロージャの左辺値を右辺値参照パラメータとして渡すことができます

lvalueラムダクロージャーは常にrvalue関数パラメーターとして渡すことができることがわかりました。

次の簡単なデモをご覧ください。

#include <iostream>
#include <functional>

using namespace std;

void foo(std::function<void()>&& t)
{
}

int main()
{
    // Case 1: passing a `lvalue` closure
    auto fn1 = []{};
    foo(fn1);                          // works

    // Case 2: passing a `lvalue` function object
    std::function<void()> fn2 = []{};
    foo(fn2);                          // compile error

    return 0;
}

ケース2は標準的な動作です(デモ目的でstd::functionを使用しただけですが、他のタイプも同じように動作します)。

ケース1はどのように、そしてなぜ機能するのですか?関数が返された後のfn1クロージャの状態は何ですか?

18
Sumudu

ケース1はどのように、そしてなぜ機能するのですか?

fooを呼び出すには、rvalue参照にバインドするstd::function<void()>のインスタンスが必要です。 std::function<void()>は、void()シグネチャと互換性のある任意の呼び出し可能なオブジェクトから構築できます。

まず、一時的なstd::function<void()>オブジェクトが_[]{}_から作成されます。使用されるコンストラクターは#5 here で、クロージャーを_std::function_インスタンスにコピーします。

_template< class F >
function( F f );
_

ターゲットをstd::move(f)で初期化します。 fが関数へのNULLポインターまたはメンバーへのNULLポインターである場合、_*this_は呼び出し後に空になります。

次に、一時的なfunctionインスタンスが右辺値参照にバインドされます。


関数が返された後のfn1クロージャの状態は何ですか?

以前と同じですが、_std::function_インスタンスにコピーされたためです。元の閉鎖は影響を受けません。

8
Vittorio Romeo

ラムダはstd::functionではありません。参照は直接をバインドしません。

ケース1は、ラムダがstd::functionsに変換できるため機能します。これは、一時的なstd::functioncopyingfn1によって具体化されることを意味します。この一時変数は右辺値参照にバインドできるため、引数はパラメーターと一致します。

また、コピーがfn1fooで発生するすべての影響を受けない理由でもあります。

関数が返された後のfn1クロージャの状態は何ですか?

fn1は何もキャプチャしないため、ステートレスです。

ケース1はどのように、そしてなぜ機能するのですか?

これは、引数が参照される右辺値の型とは異なる型であるため機能します。タイプが異なるため、暗黙的な変換が考慮されます。ラムダはこのstd::functionの引数に対してCallableであるため、std::functionのテンプレート変換コンストラクタを介して暗黙的にラムダに変換できます。変換の結果はprvalueであるため、rvalue参照とバインドできます。

5
eerorika