ラムダと同じくらい長く持続する内部値をラムダに与えるにはどうすればいいですか?

囲まれているスコープに影響を与えずに、ラムダ内で変更できる変数を使用したいのですが。このように振る舞うもの:

std::vector vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
{
  auto sum = 0;
  std::for_each(vec.begin(), vec.end(), [sum](int value) mutable
  {
    sum += value;
    std::cout << "Sum is up to: " << sum << '/n';
  });
}

しかし、ラムダの外側で sum 変数を宣言せずにそれを実行できるようにしたいです。このようなもの:

std::vector vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };

std::for_each(vec.begin(), vec.end(), [auto sum = 0](int value) mutable
{
  sum += value;
  std::cout << "Sum is up to: " << sum << '/n';
});

そのため、 sum はラムダの内側にのみ表示され、外側のスコープには表示されません。 C ++ 11/14でも可能ですか?

3
auto を削除し、 1114 に置き換えれば、問題ありません。
追加された 著者 Kerrek SB,
それはすごい!ありがとう:)
追加された 著者 Kian,
それはすごい!ありがとう:)
追加された 著者 Kian,

7 答え

C ++ 14では一般化されたLambdaキャプチャが導入されています。

auto と同様に、キャプチャはinit式のタイプから推測されます。

[sum = 0] (int value) mutable {
   //'sum' has been deduced to 'int' and initialized to '0' here.
    /* ... */
}
9
追加された
興味深いことに、その名前で誰かがこの機能を参照するのを見たのは今回が初めてです。通常私は init-capture 、または "一般化されたラムダキャプチャ"を見ます。
追加された 著者 T.C.,
@ T.C。そうです、それは一般にそれらの名前によって参照されます。編集しました。
追加された 著者 Snps,

C ++ 14では一般化されたLambdaキャプチャが導入されています。

auto と同様に、キャプチャはinit式のタイプから推測されます。

[sum = 0] (int value) mutable {
   //'sum' has been deduced to 'int' and initialized to '0' here.
    /* ... */
}
9
追加された
興味深いことに、その名前で誰かがこの機能を参照するのを見たのは今回が初めてです。通常私は init-capture 、または "一般化されたラムダキャプチャ"を見ます。
追加された 著者 T.C.,
@ T.C。そうです、それは一般にそれらの名前によって参照されます。編集しました。
追加された 著者 Snps,

C ++ 11で動けない場合(そしてC ++ 14のラムダキャプチャ式を使用できない場合は、@ Snmp's answerを参照)、他の関数の場合と同様に、ラムダの内側で静的変数を宣言できます。以下の例

#include 
#include 
#include 

int main()
{
    std::vector vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    std::for_each(vec.begin(), vec.end(), [](int value)
    {
        static decltype(vec)::value_type sum{};
        sum += value;
        std::cout << "Sum is up to: " << sum << '\n';
    });
}
0
追加された
それはまさに1つが達成しようとしていることによります。同じラムダ(つまりクロージャから生成されたオブジェクト)を「再利用」できるため、この点ではキャプチャ式を使用するほうが良いと思います。
追加された 著者 vsoftco,
@greggo の意味がよくわからない "" sum "の初期化は1回の実行で1回だけ行われるようです。静的varのように呼び出します。通常の機能で。リンクをありがとう。
追加された 著者 vsoftco,
これを "main"の代わりに "sumvec"と呼ばれる関数に入れると、 "sum"は一度だけ初期化されるので、2回目の "sumvec"呼び出しは機能しません。
追加された 著者 greggo,
それはそれを超えていると思います。どちらの場合も、 "sumarr"を呼び出すたびに新しいラムダオブジェクトが作成されますが、静的変数はすべてのインスタンスに共通です。呼び出しから呼び出しへと変わる静的ローカルは非常に限られた用途を持っています、私はそれらのうちのこれを呼びません。
追加された 著者 greggo,
私には "sum"の初期化は実行ごとに一度だけ行われるようです。この例では "main"は重要ではありませんが、一般的な場合は問題です。また、統計は再入可能性を壊します(そして最適化を制限する可能性があります)。
追加された 著者 greggo,

C ++ 11で動けない場合(そしてC ++ 14のラムダキャプチャ式を使用できない場合は、@ Snmp's answerを参照)、他の関数の場合と同様に、ラムダの内側で静的変数を宣言できます。以下の例

#include 
#include 
#include 

int main()
{
    std::vector vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    std::for_each(vec.begin(), vec.end(), [](int value)
    {
        static decltype(vec)::value_type sum{};
        sum += value;
        std::cout << "Sum is up to: " << sum << '\n';
    });
}
0
追加された
それはまさに1つが達成しようとしていることによります。同じラムダ(つまりクロージャから生成されたオブジェクト)を「再利用」できるため、この点ではキャプチャ式を使用するほうが良いと思います。
追加された 著者 vsoftco,
@greggo の意味がよくわからない "" sum "の初期化は1回の実行で1回だけ行われるようです。静的varのように呼び出します。通常の機能で。リンクをありがとう。
追加された 著者 vsoftco,
これを "main"の代わりに "sumvec"と呼ばれる関数に入れると、 "sum"は一度だけ初期化されるので、2回目の "sumvec"呼び出しは機能しません。
追加された 著者 greggo,
私には "sum"の初期化は実行ごとに一度だけ行われるようです。この例では "main"は重要ではありませんが、一般的な場合は問題です。また、統計は再入可能性を壊します(そして最適化を制限する可能性があります)。
追加された 著者 greggo,
それはそれを超えていると思います。どちらの場合も、 "sumarr"を呼び出すたびに新しいラムダオブジェクトが作成されますが、静的変数はすべてのインスタンスに共通です。呼び出しから呼び出しへと変わる静的ローカルは非常に限られた用途を持っています、私はそれらのうちのこれを呼びません。
追加された 著者 greggo,

少なくとも私の意見では、これを実行するためのより良い方法があります。 std :: for_each を使用していますが、 std :: accumulate を模倣するために使用しています。当面の仕事には後者が明確な選択です。

#include 
#include 
#include 

int main(){
    std::vector vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };

    std::accumulate(vec.begin(), vec.end(), 0, [](int sum, int val) {
        sum += val;
        std::cout << "sum is up to: " << sum << "\n";
        return sum; });
}

本当に重要かどうかはわかりませんが、C ++ 11以降の機能は必要ありません。

0
追加された
「前回の呼び出しからの値を保持し、それに操作を適用する変数」がアキュムレータの一種であることがわかります。たとえば、インデックスが必要な場合は、反復ごとに1つずつ累積して追加します。ただし、std :: accumulateはアキュムレータを返します。ラムダ内から他の構造に索引付けする必要があるために索引を使用したい場合、その名前は誤解を招く可能性があります。蓄積することは実装の詳細です、私は何も蓄積することを気にしません。 6か月後にコードを読んでいる人は、なぜあなたがaccumulateを呼び出して戻り値を破棄したのか疑問に思うかもしれません。
追加された 著者 Kian,
この例は、説明のみを目的としています。蓄積することは実際には合いません、そして名前はレビューで混乱するでしょう。ただし、アルゴリズムには他の機能もあることを覚えておくと便利です。私はいつも何かをやろうとする前にチェックしません。
追加された 著者 Kian,
@Kian:それでも、あなたが求めていたのは、あるラムダの呼び出しから次の呼び出しまで(何か)を累積するための価値でした...
追加された 著者 Jerry Coffin,

少なくとも私の意見では、これを実行するためのより良い方法があります。 std :: for_each を使用していますが、 std :: accumulate を模倣するために使用しています。当面の仕事には後者が明確な選択です。

#include 
#include 
#include 

int main(){
    std::vector vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };

    std::accumulate(vec.begin(), vec.end(), 0, [](int sum, int val) {
        sum += val;
        std::cout << "sum is up to: " << sum << "\n";
        return sum; });
}

本当に重要かどうかはわかりませんが、C ++ 11以降の機能は必要ありません。

0
追加された
「前回の呼び出しからの値を保持し、それに操作を適用する変数」がアキュムレータの一種であることがわかります。たとえば、インデックスが必要な場合は、反復ごとに1つずつ累積して追加します。ただし、std :: accumulateはアキュムレータを返します。ラムダ内から他の構造に索引付けする必要があるために索引を使用したい場合、その名前は誤解を招く可能性があります。蓄積することは実装の詳細です、私は何も蓄積することを気にしません。 6か月後にコードを読んでいる人は、なぜあなたがaccumulateを呼び出して戻り値を破棄したのか疑問に思うかもしれません。
追加された 著者 Kian,
この例は、説明のみを目的としています。蓄積することは実際には合いません、そして名前はレビューで混乱するでしょう。ただし、アルゴリズムには他の機能もあることを覚えておくと便利です。私はいつも何かをやろうとする前にチェックしません。
追加された 著者 Kian,
@Kian:それでも、あなたが求めていたのは、あるラムダの呼び出しから次の呼び出しまで(何か)を累積するための価値でした...
追加された 著者 Jerry Coffin,

C ++ 14が利用できない場合は、すべてをラムダでラップして元のラムダを返し、すぐにそれを呼び出すことができます。

std::for_each(vec.begin(), vec.end(), []()
{
    auto sum = 0;
    return [sum](int value) mutable
    {
        sum += value;
        std::cout << "Sum is up to: " << sum << '\n';
    };
}());

このようにして、一時的な sum の寿命は最小になります。

0
追加された