6

Herb Sutter の説得力のある講義Not your Father's C++に触発されて、Microsoft の Visual Studio 2010 を使用して C++ の最新バージョンをもう一度見てみることにしました。 ++11 はよく知られている上向きの funarg 問題を解決しました。私が知る限り、C++11 はこの問題を解決するために何もしていないため、「安全」ではありません。

関数が戻った後に存在しなくなるスタックフレームにローカル変数が割り当てられているため、ローカル変数への参照を返したくないため、関数は割り当てられたメモリへのダングリングポインターを返します。 -決定論的なデータの破損。C および C++ コンパイラはこれを認識しており、ローカルへの参照またはポインターを返そうとすると警告します。たとえば、このプログラム:

int &bar() {
  int n=0;
  return n;
}

Visual Studio 2010 で次の警告が表示されます。

warning C4172: returning address of local variable or temporary

ただし、C++11 のラムダを使用すると、参照によってローカル変数をキャプチャしてその参照を返すことが容易になり、結果として同等のダングリング ポインターが生成されます。fooローカル変数をキャプチャして返すラムダ関数を返す次の関数を考えてみましょうn

#include <functional>

std::function<int()> foo(int n) {
  return [&](){return n;};
}

この無害に見える関数は、メモリが安全ではなく、破損したデータの原因となります。この関数を呼び出してラムダをある場所で取得し、ラムダを呼び出してその戻り値を別の場所に出力すると、次の出力が得られます。

1825836376

さらに、Visual Studio 2010 では警告が表示されません。

これは、言語の非常に深刻な設計上の欠陥のように見えます。最も単純なリファクタリングでさえ、ラムダ クロス スタック フレームを作成し、暗黙のうちに非決定論的なデータ破損を導入する可能性があります。しかし、この問題に関する貴重な情報はほとんどないようです (たとえば、StackOverflow で "upwards funarg" や C++ を検索してもヒットしません)。人々はこれに気づいていますか?誰かが解決策に取り組んでいるか、回避策を説明していますか?

4

2 に答える 2

3

これはラムダに固有のものではありません.生涯に関しては、たくさんの悪いことをすることができます. C++11 は C++03 と比較していくつかの点で安全かもしれませんが、C++ はメモリの安全性に重点を置いていません。

C++が安全であることを望んでいないと言っているわけではありませんが、「使用しないものにはお金を払わない」という通常の哲学は、通常、安全ガードを追加する際の妨げになると思います (すべての無効なプログラムの診断の発行を妨げる可能性がある停止中の問題)。他のすべてのケースのパフォーマンスに影響を与えずに、上向きの funarg 問題解決できる場合、標準委員会は関心を持っています。(意地悪ではなく、面白​​くて難しい問題だと思います。)

あなたはキャッチアップを行っているように見えるので、これまでの著者 (および他の人) の知恵は、一般に、ラムダ式 (例: ) の参照によるキャッチオール キャプチャの使用を控え、[&, foo, bar]by- に注意することです。一般的な参照キャプチャ。ラムダ式のキャプチャ リストは、ライフタイムに注意する必要がある C++ の別の場所と考えることができます。または別の見方は、ラムダ式をファンクターのオブジェクトリテラル表記と見なすことです(実際にはそのように指定されています)。ライフタイムに関してクラス型を設計するときは注意が必要です。

struct foo {
    explicit foo(T& t)
        : ref(t)
    {}

    T& ref;
};

foo make_foo()
{
    T t;
    // Bad
    return foo { t };
    // Not altogether different from
    // return [&t] {};
}

この点で、ラムダ式は、「明らかな」悪いコードを書くことに関しては現状を変えず、既存の警告をすべて継承します。

于 2012-04-12T12:37:10.067 に答える
2

メモリ処理をまったく意識しないようにしようとすると、複雑な C++ プロジェクトで単純に作業することはできません。この種のパラダイムをより適切に目指している言語は何百もあります。C++ にガベージ コレクションがないのには理由があります。C++ を使用するシナリオには実際には適合しません。

ラムダの例では、単純な変更でラムダの例が完全に安全になると言われています。

#include <functional>

std::function<int()> foo(int n) {
  return [=](){return n;}; //now n is copied by value
}
于 2012-04-12T17:16:46.490 に答える