web-dev-qa-db-ja.com

定数参照としてのラムダキャプチャ?

ラムダ式のconst参照によってキャプチャすることは可能ですか?

たとえば、次のようにマークされた割り当てを失敗させます。

#include <cstdlib>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
    string strings[] = 
    {
        "hello",
        "world"
    };
    static const size_t num_strings = sizeof(strings)/sizeof(strings[0]);

    string best_string = "foo";

    for_each( &strings[0], &strings[num_strings], [&best_string](const string& s)
      {
        best_string = s; // this should fail
      }
    );
    return 0;
}

更新:これは古い質問なので、C++ 14にこれを支援する機能がある場合は更新するのが良いかもしれません。 C++ 14の拡張機能により、const参照により非constオブジェクトをキャプチャできますか? (2015年8月

148
John Dibling

constは、n3092の時点ではキャプチャの文法に含まれていません。

capture:
  identifier
  & identifier
  this

テキストには、コピーによるキャプチャと参照によるキャプチャのみが記載されており、const-nessは一切記載されていません。

私には見落としがあるように感じますが、私は標準化プロセスにあまり厳密には従っていません。

112
Steve M

C++ 14:

[&best_string = static_cast<const std::string&>(best_string)](const string& s)
{
    best_string = s; // fails
};

[〜#〜] demo [〜#〜]


C++ 17:

[&best_string = std::as_const(best_string)](const string& s)
{
    best_string = s; // fails
};

DEMO 2

86
Piotr Skotnicki

キャプチャ手段はキャプチャ手段としてconstを指定すべきではなく、外部スコープ変数にアクセスする方法のみが必要だと思います。

指定子は、外側のスコープでより適切に指定されます。

const string better_string = "XXX";
[&better_string](string s) {
    better_string = s;    // error: read-only area.
}

ラムダ関数はconst(スコープ内の値を変更できない)であるため、値ごとに変数をキャプチャすると、変数変更することはできませんが、参照はラムダスコープにありません。

13
zhb

ファンクタのパラメータとして変数を使用していない場合は、現在の関数のアクセスレベルを使用する必要があります。すべきでないと思われる場合は、ラムダをこの関数から分離してください。それはその一部ではありません。

とにかく、代わりに別のconst参照を使用することで、同じことを簡単に実現できます。

#include <cstdlib>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
    string strings[] = 
    {
        "hello",
        "world"
    };
    static const size_t num_strings = sizeof(strings)/sizeof(strings[0]);

    string best_string = "foo";
    const string& string_processed = best_string;

    for_each( &strings[0], &strings[num_strings], [&string_processed]  (const string& s)  -> void 
    {
        string_processed = s;    // this should fail
    }
    );
    return 0;
}

しかし、これは、ラムダを現在の関数から分離して、非ラムダにする必要があると仮定するのと同じです。

8
Klaim

次の3つのオプションがあると思います。

  • const参照を使用しないで、コピーキャプチャを使用する
  • 変更可能であるという事実を無視する
  • std :: bindを使用して、const参照を持つバイナリ関数の1つの引数をバインドします。

コピーを使用して

コピーキャプチャを使用したラムダの興味深い点は、それらが実際に読み取り専用であるため、希望どおりに動作することです。

int main() {
  int a = 5;
  [a](){ a = 7; }(); // Compiler error!
}

std :: bindを使用する

std::bindは、関数のアリティを減らします。ただし、これは、関数ポインタを介した間接的な関数呼び出しにつながる可能性があることに注意してください。

int main() {
  int a = 5;
  std::function<int ()> f2 = std::bind( [](const int &a){return a;}, a);
}
5
Alex

もっと短い方法があります。

「best_string」の前にアンパサンドがないことに注意してください。

「const std :: reference_wrapper << T >>」タイプになります。

[best_string = cref(best_string)](const string& s)
{
    best_string = s; // fails
};

http://coliru.stacked-crooked.com/a/0e54d6f9441e6867

3
Sergey Palitsin

Constを使用すると、アルゴリズムにアンパサンドが設定され、文字列が元の値に設定されます。言い換えると、ラムダは関数のパラメーターとして自身を定義しませんが、周囲のスコープには追加の変数があります...ただし、通常の[&、&best_string](string const s)Thereforeとして文字列を定義しません。おそらくそれをそのままにして、参照をキャプチャしようとする方が良いでしょう。

0
Saith

Clangを使用するか、このgccのバグが修正されるまで待ちます:バグ70385:const参照の参照によるLambdaキャプチャが失敗します[ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70385 ]

0
user1448926