4

標準は、このコードで何が起こるかを定義していますか?

#include <iostream>

template <typename Func>
void callfunc(Func f)
{
   ::std::cout << "In callfunc.\n";
    f();
}

template <typename Func>
void callfuncref(Func &f)
{
   ::std::cout << "In callfuncref.\n";
    f();
}

int main()
{
    int n = 10;
    // n is captured by value, and the lambda expression is mutable so
    // modifications to n are allowed inside the lambda block.
    auto foo = [n]() mutable -> void {
       ::std::cout << "Before increment n == " << n << '\n';
       ++n;
       ::std::cout << "After increment n == " << n << '\n';
    };
    callfunc(foo);
    callfunc(foo);
    callfuncref(foo);
    callfunc(foo);
    return 0;
}

これを g++ で出力すると、次のようになります。

$ ./a.out 
In callfunc.
Before increment n == 10
After increment n == 11
In callfunc.
Before increment n == 10
After increment n == 11
In callfuncref.
Before increment n == 10
After increment n == 11
In callfunc.
Before increment n == 11
After increment n == 12

この出力のすべての機能が標準で要求されていますか?

特に、ラムダ オブジェクトのコピーが作成されると、キャプチャされたすべての値もコピーされるようです。ただし、ラムダ オブジェクトが参照によって渡される場合、キャプチャされた値はコピーされません。また、関数が呼び出される直前にキャプチャされた値のコピーは作成されないため、キャプチャされた値への変更は呼び出し間で保持されます。

4

3 に答える 3

9

ラムダの型は、本体を実行するクラス (n3290 §5.1.2/3)operator()と、暗黙のコピー コンストラクター (/19) を備えた単純なクラスであり、コピーによる変数のキャプチャはコピーと同等です。 -このクラスの非静的データ メンバー (/14) に初期化 (/21) し、その変数の使用ごとに、対応するデータ メンバー (/17) に置き換えます。この変換の後、ラムダ式はこのクラスの単なるインスタンスになり、C++ の一般規則に従います。

つまり、コードは次のように機能します。

int main()
{
    int n = 10;

    class __Foo__           // §5.1.2/3
    {
        int __n__;          // §5.1.2/14
    public:
        void operator()()   // §5.1.2/5
        {
            std::cout << "Before increment n == " << __n__ << '\n';
            ++ __n__;       // §5.1.2/17
            std::cout << "After increment n == " << __n__ << '\n';
        }
        __Foo__() = delete;
        __Foo__(int n) : __n__(n) {}
      //__Foo__(const __Foo__&) = default;  // §5.1.2/19
    }
    foo {n};                // §5.1.2/21

    callfunc(foo);
    callfunc(foo);
    callfuncref(foo);
    callfunc(foo);
}

そして、ここで何をするかは明らかcallfuncrefです。

于 2012-04-09T13:36:56.103 に答える
4

ラムダを手動で構造体/クラスに展開することで、この動作を理解するのが最も簡単だと思います。これは、多かれ少なかれ次のようになります (n値によってキャプチャされるため、参照によるキャプチャは少し異なります)。

class SomeTemp {
    mutable int n;
    public:
    SomeTemp(int n) : n(n) {}
    void operator()() const
    {
       ::std::cout << "Before increment n == " << n << '\n';
       ++n;
       ::std::cout << "After increment n == " << n << '\n';
    }
} foo(n);

あなたの機能は、多かれ少なかれこのタイプのオブジェクトを操作しますcallfunccallfuncrefそれでは、あなたが行う呼び出しを調べてみましょう。

callfunc(foo);

ここでは値渡しなのでfoo、デフォルトのコピー コンストラクターを使用してコピーされます。の操作callfuncは、コピーされた値の内部状態にのみ影響し、実際の foo オブジェクトの状態は変更されません。

callfunc(foo);

同じもの

callfuncref(foo);

ああ、ここでは foo を参照渡しするので、callfuncref(これは を呼び出します)一時的なコピーではなく、実際のオブジェクトoperator()を更新します。fooこれは後で更新されることにnなりますが、これは通常の動作です。したがって、これを再度呼び出すと、次のようになります。foo11pass-by-reference

callfunc(foo);

foo再びコピーを操作しますが、 whereのコピーはn11 に設定されています。

于 2012-04-09T13:32:31.650 に答える
2

[&]明示的にすべてをキャプチャするか、参照によって特定の変数をキャプチャしない限り、値はコピーによってキャプチャされます[&n]。したがって、出力全体が標準です。

于 2012-04-09T13:12:52.727 に答える