2015-07-17 5 views
13

Beachten Sie Folgendes:Lambda Capture nach Wert veränderbar funktioniert nicht mit const &?

void test(const int &value) 
{ 
    auto testConstRefMutableCopy = [value]() mutable { 
     value = 2; // compile error: Cannot assign to a variable captured by copy in a non-mutable lambda 
    }; 

    int valueCopy = value; 
    auto testCopyMutableCopy = [valueCopy]() mutable { 
     valueCopy = 2; // compiles OK 
    }; 
} 

Warum ist die erste Version der Kompilierung ein Fehler auf, wenn ich die Lambda als wandelbar deklariert haben und gefangen value nach Wert (was ich eine Kopie davon gemacht gedacht)?

Getestet mit clang (x86_64-apple-darwin14.3.0), wo die Fehlermeldung kommt, und Visual C++ (vc120).

+0

GCC trunk, too ("Fehler: Zuweisung der schreibgeschützten Variablen 'value'") –

+1

Die 'const'-ness der Quellvariable scheint die 'veränderliche' Eigenschaft des Lambda zu übertrumpfen. Wenn Sie es in '[value = value]' ändern, wird der Fehler in gcc behoben. –

Antwort

16

[C++11: 5.1.2/14]:An entity is captured by copy if it is implicitly captured and the capture-default is = or if it is explicitly captured with a capture that does not include an &. For each entity captured by copy, an unnamed non-static data member is declared in the closure type. The declaration order of these members is unspecified. The type of such a data member is the type of the corresponding captured entity if the entity is not a reference to an object, or the referenced type otherwise.[..]

Die Art der value in Ihrem Lambda const int, da sie von der Kopie von einem const int& gefangen genommen wurden.

So, obwohl die Call-Betreiber-Funktion des Lambda ist nicht const (Sie die Lambda-mutable markiert), die tatsächliche implizite Mitglied value ist vom Typ const int und nicht mutiert werden kann.

Offen gesagt scheint dies absurd; Ich würde erwarten, dass diese Regel besagt, dass der referenzierte Typ constness verliert, da es sich um eine Kopie handelt. Das Vorhandensein oder Fehlen des Schlüsselworts mutable auf dem Lambda selbst (und somit das Vorhandensein oder Fehlen des Schlüsselwortes const auf der generierten Aufrufoperatorfunktion) sollte hier die einzige Zugriffskontrolle sein.

In C++ 14 können Sie dies umgehen, indem Sie als [value=value] erfassen, die die gleichen Regeln wie auto verwendet und damit die const fällt. C++ ist großartig, nicht wahr?

+0

Würde dies nicht die Annahme/Absicht des Aufrufers außer Kraft setzen? –

+0

@underscore_d: Der Anrufer schrieb "veränderbar". Meiner Meinung nach ist diese Absicht ziemlich klar und das macht das Mitglied selbst "const" ist viel zu viel. –

+0

Ja, jetzt, wo ich aufgewacht bin und festgestellt habe, dass es werthaltig ist, sehe ich, was du meinst. Das sieht nach einer weiteren Mehrdeutigkeit aus, die dadurch entsteht, dass der Oktopus zu viele Beine hat ... –

4

mutable ermöglicht es einem Lambda, die Kopie eines nicht-konstanten Parameters zu ändern, der von der Kopie erfasst wurde, aber es erlaubt keine const Parameter.

So funktioniert dieser Code (und Ausgänge inside 2 outside 1):

int a = 1; 
[a]() mutable { 
    a = 2; // compiles OK 
    cout << "inside " << a << "\n"; 
}(); 
cout << " outside " << a << "\n"; 

Aber wenn wir mutable weglassen oder eine const int machen, gibt der Compiler einen Fehler.

In unserem Fall ist das erste Lambda gibt einen Fehler, da valueconst ist:

void test(const int &value) 

Wenn wir copyValue machen const:

const int valueCopy = value; 

dann der gleiche Fehler wird mit dem zweiten Lambda auftreten.

Verwandte Themen