webentwicklung-frage-antwort-db.com.de

Lambda-Capture als konstante Referenz?

Ist es möglich, durch einen konstanten Verweis in einem Lambda-Ausdruck zu erfassen?

Ich möchte, dass die unten markierte Zuordnung beispielsweise fehlschlägt:

#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;
}

Update: Da dies eine alte Frage ist, kann es nützlich sein, sie zu aktualisieren, wenn in C++ 14 Möglichkeiten vorhanden sind, um dies zu unterstützen. Erlauben uns die Erweiterungen in C++ 14, ein Nicht-const-Objekt anhand einer const-Referenz zu erfassen? ( August 2015 )

135
John Dibling

const ist ab n3092 nicht in der Grammatik für Captures enthalten:

capture:
  identifier
  & identifier
  this

Der Text erwähnt nur die Erfassung durch Kopie und die Erfassung durch Referenz und keine Art von Konstanz.

Fühlt sich für mich wie ein Versehen an, aber ich habe den Standardisierungsprozess nicht sehr genau verfolgt.

102
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

70
Piotr Skotnicki

Ich denke, der Capture-Teil sollte nicht const angeben, da das Capture-Mittel nur eine Möglichkeit zum Zugriff auf die äußere Gültigkeitsbereichsvariable benötigt.

Der Bezeichner wird besser im äußeren Bereich angegeben. 

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

lambda functionist const (Wert kann nicht in seinem Gültigkeitsbereich geändert werden). Wenn Sie also eine Variable nach Wert erfassen, kann die Variable nicht geändert werden, die Referenz befindet sich jedoch nicht im Lambda-Gültigkeitsbereich. 

12
zhb

Ich denke, wenn Sie die Variable nicht als Parameter des Funktionsgebers verwenden, sollten Sie die Zugriffsebene der aktuellen Funktion verwenden. Wenn Sie der Meinung sind, dass Sie dies nicht tun sollten, trennen Sie Ihr Lambda von dieser Funktion, es gehört nicht dazu.

Auf jeden Fall können Sie einfach dasselbe erreichen, indem Sie stattdessen eine andere const-Referenz verwenden: 

#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;
}

Das ist jedoch dasselbe, als würde man annehmen, dass Ihr Lambda von der aktuellen Funktion isoliert sein muss, was es zu einem Nicht-Lambda macht.

8
Klaim

Ich denke, Sie haben drei verschiedene Möglichkeiten:

  • verwenden Sie keine const-Referenz, sondern verwenden Sie eine Kopie
  • ignorieren Sie die Tatsache, dass es modifizierbar ist 
  • verwenden Sie std :: bind, um ein Argument einer binären Funktion zu binden, die über eine const-Referenz verfügt.

eine Kopie verwenden

Das Interessante an Lambdas mit Copy-Captures ist, dass diese nur gelesen werden können und daher genau das tun, was Sie möchten. 

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

mit std :: bind

std::bind reduziert die Arity einer Funktion. Beachten Sie jedoch, dass dies/kann zu einem indirekten Funktionsaufruf über einen Funktionszeiger führen.

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

Es gibt einen kürzeren Weg.

Beachten Sie, dass vor "best_string" kein Et-Zeichen steht.

Es wird vom Typ "const std :: reference_wrapper << T >>" sein.

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

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

3
Sergey Palitsin

Wenn Sie ein const verwenden, wird der Algorithmus einfach mit einem kaufmännischen Und-Zeichen auf den ursprünglichen Wert gesetzt Mit anderen Worten, das Lambda definiert sich nicht wirklich als Parameter der Funktion, obwohl der umgebende Bereich eine zusätzliche Variable hat ... Ohne es zu definieren, würde es die Zeichenfolge nicht als das typische .__ definieren. [&, & best_string] (string const s) Daher ist am wahrscheinlichsten besser, wenn wir es dabei belassen und versuchen, die Referenz zu erfassen.

0
Saith

Verwenden Sie clang oder warten Sie, bis dieser gcc-Fehler behoben ist: Fehler 70385: Lambda-Capture durch Referenz der const-Referenz schlägt fehl [ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70385 ]

0
user1448926