C++ 11ラムダは素晴らしいです!C++ 11のラムダ構文では、ヒープ割り当てのクロージャ?
しかし、変更可能なデータを安全に処理する方法の1つが欠落しています。
以下は、最初のカウントの後に悪いカウントを与える:
#include <cstdio>
#include <functional>
#include <memory>
std::function<int(void)> f1()
{
int k = 121;
return std::function<int(void)>([&]{return k++;});
}
int main()
{
int j = 50;
auto g = f1();
printf("%d\n", g());
printf("%d\n", g());
printf("%d\n", g());
printf("%d\n", g());
}
を与え、
$ g++-4.5 -std=c++0x -o test test.cpp && ./test
121
8365280
8365280
8365280
理由はf1()
戻った後、k
がスコープの外に、まだスタックにあるということです。したがって、最初にg()
が実行されたのはk
ですが、それ以降はスタックが破損し、k
の値が失われます。だから、
、私はC++ 11で安全に通いクロージャを作ることができた唯一の方法は、ヒープ上に明示的に閉じられた変数を割り当てることである。ここでは
std::function<int(void)> f2()
{
int k = 121;
std::shared_ptr<int> o = std::shared_ptr<int>(new int(k));
return std::function<int(void)>([=]{return (*o)++;});
}
int main()
{
int j = 50;
auto g = f2();
printf("%d\n", g());
printf("%d\n", g());
printf("%d\n", g());
printf("%d\n", g());
}
、[=]
は、共有ポインタを確実にするために使用されますがコピーされたは参照されないため、メモリの処理は正しく行われます。k
のヒープ割り当てコピーは、生成された関数g
が有効範囲外になると解放されます。希望通りの結果が
$ g++-4.5 -std=c++0x -o test test.cpp && ./test
121
122
123
124
、あるそれはそれらを参照解除することにより、変数を参照するためにはかなり醜いですが、代わりに参照を使用することが可能です:
std::function<int(void)> f3()
{
int k = 121;
std::shared_ptr<int> o = std::shared_ptr<int>(new int(k));
int &p = *o;
return std::function<int(void)>([&]{return p++;});
}
実は、これは奇妙なことに、私に
を与えます$ g++-4.5 -std=c++0x -o test test.cpp && ./test
0
1
2
3
理由は何ですか?共有ポインタの参照を取得するのは丁寧ではないかもしれません。追跡された参照ではないので、今考えてみましょう。私は、ラムダの内側への参照を移動すると自動的に経由して安全に通いクロージャを作るための方法があった場合、それはいいだろういずれにしてもクラッシュ、
std::function<int(void)> f4()
{
int k = 121;
std::shared_ptr<int> o = std::shared_ptr<int>(new int(k));
return std::function<int(void)>([&]{int &p = *o; return p++;});
}
寄付、
g++-4.5 -std=c++0x -o test test.cpp && ./test
156565552
/bin/bash: line 1: 25219 Segmentation fault ./test
の原因となることがわかりましたヒープ割り当て。たとえば、[=]
と[&]
の代わりに、変数がヒープ割り当てされ、共有ポインタへの参照を介して参照される必要があることが示されている場合。私が最初に考えたのは、std::function
について学んだときに、クロージャをカプセル化するオブジェクトを作成していたため、クロージャ環境のストレージを提供することができましたが、私の実験ではこれは役に立たないようです。
私はC++ 11で安全にリターン可能なクロージャが重要な役割を果たしていると考えています。
Valgrindの内部では、このようなテストを実行する必要があります。これは、割り当て解除されたメモリにアクセスする際に正しく動作することを確認しているからです。 – Potatoswatter
static int k = 121; – adnako