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


166

ラムダ式で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月


あなたのラムダは次のようにすべきではありません:[&, &best_string](string const s) { ...}
erjot 2010

3
本当に矛盾したキャプチャ。"const&"は、ラムダ関数でアクセスする必要があるが変更してはならない大きなconstオブジェクトがある場合に非常に便利です
sergtk

コードを見てください。2つのパラメーターlambdaを使用して、2番目のパラメーターをconst参照としてバインドできます。ただし、費用がかかります。
Alex

1
これはC ++ 11では不可能のようです。しかし、おそらくこの質問をC ++ 14用に更新できます-これを可能にする拡張機能はありますか?C ++ 14の一般化されたラムダキャプチャ?
Aaron McDaid、2015

回答:


127

const n3092現在のキャプチャの文法にはありません:

capture:
  identifier
  & identifier
  this

このテキストは、コピーによるキャプチャと参照によるキャプチャについてのみ言及し、いかなる種類の一貫性についても言及していません。

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


47
変更可能だったキャプチャから変更された変数に戻るバグを追跡しましたが、そうである必要がありましたconst。より正確には、キャプチャ変数がのconst場合、コンパイラはプログラマに正しい動作を強制していました。構文がサポートされていると便利です[&mutableVar, const &constVar]
Sean

これはC ++ 14で可能であるように見えますが、動作させることができません。助言がありますか?
Aaron McDaid 2015

37
Constnessは、キャプチャされた変数から継承されます。したがって、aとしてキャプチャする場合は、ラムダの前constに宣言してキャプチャしますconst auto &b = a;b
StenSoft

7
@StenSoft Bleargh。参照によってメンバー変数をキャプチャする場合、明らかにこれは当てはまりません。関数の[&foo = this->foo]内部ではconstキャプチャ自体が修飾子を破棄するというエラーが表示されます。これはGCC 5.1のバグかもしれませんが、私はそう思います。
カイルストランド

119

static_cast/ を使用const_cast

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

デモ


を使用してstd::as_const

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

デモ2


また、おそらくこれを受け入れられた回答に編集する必要がありますか?いずれにせよ、c ++ 11とc ++ 14の両方をカバーする1つの良い答えがあるはずです。私はC ++ 14は、今後数年間誰にとっても十分良いだろうと主張することができると思います、が
アーロンMcDaid

12
@AaronMcDaid const_castは、無条件に揮発性オブジェクトをconstオブジェクトに変更できます(にキャストするように要求された場合const)。したがって、私が好む制約を追加するにはstatic_cast
Piotr Skotnicki

1
一方、@ PiotrSkotnickiでは、static_cast型を正確に取得しなかった場合、参照をconstで暗黙的に一時的に作成する可能性があります
MM

24
@MM &basic_string = std::as_const(best_string)はすべての問題を解決する必要があります
Piotr Skotnicki '10年

14
書き込みに恐ろしい方法であること、何かその問題を除き@PiotrSkotnicki なければならないような単純なことconst& best_string
カイルストランド

12

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

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

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

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


1
@Amarnath Balasubramani:私の意見ですが、ラムダキャプチャパーツでconst参照を指定する必要はないと思います。なぜ、ここに変数constがあり、別の場所にconstがなければならないのですか(可能であれば、エラーが発生しやすくなります) )。とにかくあなたの応答を見て幸せ。
zhb

2
better_string包含スコープ内で変更する必要がある場合、このソリューションは機能しません。const-refとしてキャプチャする使用例は、変数がラムダ内ではなく、包含スコープで変更可能である必要がある場合です。
ジョナサンシャーマン2017年

@JonathanSharman、あなたが作ることができますので、変数へのconst参照を作成するために、あなたには何もかからないconst string &c_better_string = better_string;し、喜んでラムダに渡す:[&c_better_string]
スティード

@Steed問題は、周囲のスコープに変数名を追加することです。上記のPiotr Skotnickiの解決策は、変数のスコープを最小限に保ちながらconst-correctnessを達成するため、最もクリーンだと思います。
ジョナサンシャーマン2017年

@JonathanSharman、ここで私たちは意見の領域に入ります-最もきれいな、または最もきれいなものなど。私のポイントは、両方のソリューションがタスクに適しているということです。
2017年

8

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

とにかく、代わりに別の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;
}

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


1
捕獲条項はまだ言及しているbest_stringだけです。それとは別に、GCC 4.5は意図したとおりにコードを「正常に拒否」します。
selibitze

はい、これは私が技術レベルで達成しようとしていた結果を私に与えます。しかし、結局のところ、私の元の質問に対する答えは「いいえ」のようです。
John Dibling 2010

なぜそれが「非ラムダ」になるのでしょうか?

ラムダの性質は、コンテキストに依存するからです。特定のコンテキストが必要ない場合は、ファンクタを作成するための簡単な方法です。ファンクタがコンテキストに依存しない場合は、それを実際のファンクタにします。
Klaim

3
「ファンクタがコンテキスト非依存である必要がある場合、それを本当のファンクタにする」...そしてキスしてインラインでさようなら?
Andrew Lazarus 14

5

私はあなたが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);
}

1
包含スコープ内の変数への変更を除いて、ラムダには反映されません。これは参照ではなく、単なる再割り当てではない変数です。なぜなら、再割り当てはそれが意味するように見えるものを意味しないからです。
Grault、2015


0

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


1
このリンクで質問に答えることができますが、回答の重要な部分をここに含め、参照用のリンクを提供することをお勧めします。リンクされたページが変更されると、リンクのみの回答が無効になる可能性があります。」
Div 2016年

わかりました、私はここにgccバグの説明を追加するために私の答えを編集しました。
user1448926 2016年

これは、もしあれば、かなり間接的な回答です。バグは、constをキャプチャするときにコンパイラがどのように失敗するかに関するものです。おそらく、問題の問題に対処または回避するための何らかの方法がgccで機能しない場合があるのはなぜですか。
Stein

0

constを使用すると、単純にアルゴリズムがアンパサンドして文字列を元の値に設定します。つまり、ラムダは実際には関数のパラメーターとしてそれ自体を定義しませんが、周囲のスコープには追加の変数があります...定義せずにただし、文字列を典型的な[&、&best_string](string const s) として定義することはできません。 そのため、そのままにして参照をキャプチャしようとするのが最も良い方法です。


これは非常に古い質問です。あなたの回答には、参照しているC ++バージョンに関連するコンテキストが欠けています。このコンテンツを提供してください。
ZF007 2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.