Forum: PC-Programmierung C++14 init capture einer Referenz / this


von Timm R. (Firma: privatfrickler.de) (treinisch)


Lesenswert?

Hallo,

ich mache im Moment kleine Progrämmchen um mich ein wenig in modernem 
C++ zu üben.

Dabei habe ich folgendes Lambda getippt, eigentlich nur um zu 
bestätigen, dass es nicht geht, es funktioniert aber:
1
levelSliderComponent_.setCallback([&level=level](float inLevel) {level=inLevel;});

float level; ist ein Member der Klasse, in deren Konstruktor ich obige 
Zeile geschrieben habe.

Ich hätte erwartet, dass das ohne this nicht so geht, die Version
1
levelSliderComponent_.setCallback([&level](float inLevel){level=inLevel;});

funktioniert erwartungsgemäß nicht.

Meine Frage: Was ist der genaue Grund, warum das funktioniert? Soll das 
funktionieren, oder ist das „Zufall"?

Vlg
 Timm

: Bearbeitet durch User
von Sebastian V. (sebi_s)


Lesenswert?

Ja das soll so funktionieren. Eigentlich muss man für den Zugriff auf 
Membervariablen this capturen, wenn man einen Initializer in seinem 
Capture nutzt ist das aber wie eine neue Variable. In diesem Fall 
vielleicht etwas verwirrend weil du den gleichen Namen benutzt. 
Vielleicht findest du
1
levelSliderComponent_.setCallback([&l=level](float inLevel) {l=inLevel;});
 etwas weniger verwirrend.

Nachtrag:
Statt mit einfach nur einer Variable kannst du auch mit beliebig 
komplizierten Ausdrücken initialisieren:
1
[x = foo(bar->fooBar)]{ /* use x */ }

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Sebastian V. schrieb:
> Ja das soll so funktionieren. Eigentlich muss man für den Zugriff auf
> Membervariablen this capturen

... oder einen default-capture verwenden.

Die einzelnen Elemente in der capture-list müssen entweder

- automatische Variablen ( != member-variables),
- this oder
- ein default-capture

sein.

von Timm R. (Firma: privatfrickler.de) (treinisch)


Lesenswert?

Hallo,

vielen Dank euch!

@Sebastian

ach das mit dem level=level ist nicht das Problem, ist ja ein = 
dazwischen, da sind Quelle und Ziel ja eindeutig, das mit den 
komplexeren Ausdrücken ist interessant, ich kannte schon die Move 
Capture, aber das es soweit geht war mir nicht klar, definitiv nice to 
know!

@Wilhelm
Scott Meyers empfiehlt default capture zu vermeiden, da wollte ich mich 
erstmal dran halten, zumindest, bis mein Handicap besser ist :-)

Aber mein Thema ist auch an dieser Stelle eher nicht, wie das geht, 
sondern warum.



Vielleicht darf ich noch mal nachhaken? Ich glaube, dass mir noch ein 
Quentchen zum Verständnis fehlt. Wie gesagt nicht, was die 
Funktionalität angeht, ich weiß jetzt, was ich tippen muss, aber der 
Hintergrund liegt noch etwas im Dunkel.

Wenn ich das richtig sehe (?) verhält sich die init capture „normal”. 
Analog zu: Ich kann ja über eine normale Referenz oder einen Pointer 
auch von außen in eine member-Variable schreiben, das ist ja eigentlich 
ganz normal.

Aber was ist mit dieser C++11 capture los? Da fehlt mir noch ein 
Schritt. Mir ist klar, dass die C++11 capture sich nur auf den lokalen 
Scope bezieht und das this als nicht namentlich aufgeführter Parameter 
an die member Funktion übergeben wurde, so dass immer wenn ich level 
schreibe eigentlich this->level da steht, aber warum kann ein return 
level; eine ohne this gültige Referenz liefern, das [&level] verreckt 
aber auf halber Strecke?

Ich vermute es gibt einen Satz, der es mir wie Schuppen von den Augen 
fallen lässt?

Vielen Dank

 Timm

: Bearbeitet durch User
von Guest (Gast)


Lesenswert?

levelSliderComponent_.setCallback([&level=level](float inLevel) 
{level=inLevel;});

legt eine neue Variable level an die nichts mit der Membervariable level 
zu tun hat.

von Timm R. (Firma: privatfrickler.de) (treinisch)


Lesenswert?

Hallo,

Guest schrieb:
> levelSliderComponent_.setCallback([&level=level](float inLevel)
> {level=inLevel;});
>
> legt eine neue Variable level an die nichts mit der Membervariable level
> zu tun hat.

Stimmt das denn? Nichts miteinander zu tun haben heißt doch, nichts 
miteinander zu tun haben!

Wenn ich jetzt doch mal Sebastians Version verwende:

[&l=level]

dann kann ich ja über l im Lambda in die member variable level schreiben 
und sie auch auslesen, bei meiner Version werde ich bei jedem Aufruf des 
lambdas einen anderen Wert für l haben, mehr "mit der Membervariablen zu 
tun haben" geht doch kaum?

vlg
 Timm

von Sebastian V. (sebi_s)


Lesenswert?

Timm R. schrieb:
> mehr "mit der Membervariablen zu
> tun haben" geht doch kaum?

Naja gut, es ist eine Referenz auf die Membervariable, aber es ist eben 
nicht die Membervariable selbst die du da benutzt. Darum brauchst du 
auch kein this capturen. Das initialisieren im Capture ist im Verhalten 
fast identisch zu:
1
auto &l = level;
2
levelSliderComponent_.setCallback([&l](float inLevel) {l=inLevel;});
Einiziger Unterschied ist, dass die Variable l beim init Capture eben 
nur innerhalb des Lambdas gültig ist.

von Timm R. (Firma: privatfrickler.de) (treinisch)


Lesenswert?

Hallo Sebastian,

ah, jetzt ja! Danke!
Das Beispiel war erhellend!

Das wars.

vlg
 Timm

von Vincent H. (vinci)


Lesenswert?

Timm R. schrieb:
> Scott Meyers empfiehlt default capture zu vermeiden, da wollte ich mich
> erstmal dran halten, zumindest, bis mein Handicap besser ist :-)

Ich kann mich an das Kapitel grad nicht komplett erinnern, würde das 
aber (ausnahmsweise) nicht direkt unterschreiben. Wenn du anfängst mehr 
als eine Variable des Members zu benötigen, ist ein ein [this] capture 
geeigneter.

Soweit ich weiß sind Compiler aktuell nicht smart genug um "unnötige" 
Referenzen wegzuoptimieren. Das könnte auf einer langsameren und 
kleineren Embedded Platform durchaus wehtun... vor allem dann wenn man 
die Lambda Funktion irgendwo speichern will, etwa in einem std::function 
Objekt. Wenn man nicht aufpasst wird in so einem Fall dann sogar noch 
dynamisch Speicher alloziert, weil die ganzen Referenzen nicht ins 
Objekt passen (Stichwort "small object optimization").

von Timm R. (Firma: privatfrickler.de) (treinisch)


Lesenswert?

Hallo Vincent,

Vincent H. schrieb:
> Timm R. schrieb:
>> Scott Meyers empfiehlt default capture zu vermeiden, da wollte ich mich
>> erstmal dran halten, zumindest, bis mein Handicap besser ist :-)
>
> Ich kann mich an das Kapitel grad nicht komplett erinnern, würde das
> aber (ausnahmsweise) nicht direkt unterschreiben. Wenn du anfängst mehr
> als eine Variable des Members zu benötigen, ist ein ein [this] capture
> geeigneter.

[this] ist doch keine default capture? Wenn ich das richtig verstehe 
sind [=] und [&] die default captures modes? Er schreibt "There are two 
default capture modes in C++11: by-reference and by-value." Er schreibt, 
man solle lieber explicit capture, wie zB [&divisor] verwenden, darunter 
hätte ich auch [this] verstanden, denn expliziter geht´s ja nun nicht?

vlg
 Timm

von Vincent H. (vinci)


Lesenswert?

Erm, das stimmt natürlich!
Keine Ahnung wie ich grad auf die Idee kam...

Der Rest bezüglich "wenn mehrere Member dann this", ist aber hoffentlich 
weniger Topfen. ;)

Beitrag #5137839 wurde von einem Moderator gelöscht.
Beitrag #5138077 wurde von einem Moderator gelöscht.
Beitrag #5140110 wurde von einem Moderator gelöscht.
Beitrag #5140392 wurde von einem Moderator gelöscht.
Beitrag #5142605 wurde von einem Moderator gelöscht.
Beitrag #5143778 wurde von einem Moderator gelöscht.
Beitrag #5145244 wurde von einem Moderator gelöscht.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.