`this` inside lambda body
-
Normally you can use a lambda where you could use a function/method and they work in essentially the same way as each other, they are interchangeable. There is something about
this
for the body of the lambda that I cannot quite get my ahead around.slotObject->setObjectName("slotObject"); connect(signalObject, &SignalClass::signalMethod, slotObject, &SlotClass::slotMethod); void SlotClass::slotMethod() { qDebug() << this->objectName(); }
slotObject
is thethis
in the body ofSlotClass::slotMethod()
---connect()
uses something likestd::function()
to set up to call asslotObject->slotMethod()
.Why is this not the case for a lambda slot?
connect(signalObject, &SignalClass::signalMethod, slotObject, []() { qDebug() << this->objectName(); } );
won't work (or compile) as-is. It is not doing some equivalent of
slotObject->lambda()
.While
&SlotClass::slotMethod
is a member method, and is called withslotObject
asthis
, the lambda is a free function. How does this come about? Doesconnect()
recognise this and have a different code path to implement? Doesstd::function()
(if that's whatconnect()
uses) handle this? -
@Chris-Kawa said in `this` inside lambda body:
Also, because I didn't mention it above, slotObject in case of free functions or lambdas is not used for executing them at all. It's just for scope of the connection, so that it disconnects when that object goes away, but it has nothing to do with the invoked function or lambda
Hmm, ISTM that is not true. In the case of a member method (not lambda), for
connect(signalObject, &SignalClass::signalMethod, slotObject, &SlotClass::slotMethod);
the code will effectively runslotObject->slotMethod()
. OIC, here you say "in case of free functions or lambdas", yes in those cases (instead of member method). That is what I was asking about, why it was different.So you can pass what you like for the context to a lambda, but you cannot make a lambda act as an implicit member method even if you want to, it's stuck as a free function, is that the case?
@JonB said:
Hmm, ISTM that is not true.
Yes, I said the object is not related to free functions/lambdas. In case of member functions absolutely yes, it does double duty as the scope object and the object the member function is executed on.
you cannot make a lambda act as a member method even if you want to, it's stuck as a free function, is that the case?
Yes. Lambdas are a bit weird beasts. Like the dual nature of light they have a double identity - they behave like free functions and even decay to a free function pointer, but they are also like an unnamed class with () operator in that they store captured values in their scope, like class members. But they have no explicit type and they don't have their own
this
. When you invoke a lambda there is no hidden 'this' parameter passed like in case of class member functions.Unlike some other languages C++ doesn't allow you to modify the class at runtime, i.e. you can't tack on a random function to a class instance. That also goes for lambdas. You can't do
obj->someFunctionThatIsNotAMember()
orobj->someLambda
and have them getobj
asthis
. As always there are standard proposals though (I doubt they will ever get accepted, but who knows) :) -
Normally you can use a lambda where you could use a function/method and they work in essentially the same way as each other, they are interchangeable. There is something about
this
for the body of the lambda that I cannot quite get my ahead around.slotObject->setObjectName("slotObject"); connect(signalObject, &SignalClass::signalMethod, slotObject, &SlotClass::slotMethod); void SlotClass::slotMethod() { qDebug() << this->objectName(); }
slotObject
is thethis
in the body ofSlotClass::slotMethod()
---connect()
uses something likestd::function()
to set up to call asslotObject->slotMethod()
.Why is this not the case for a lambda slot?
connect(signalObject, &SignalClass::signalMethod, slotObject, []() { qDebug() << this->objectName(); } );
won't work (or compile) as-is. It is not doing some equivalent of
slotObject->lambda()
.While
&SlotClass::slotMethod
is a member method, and is called withslotObject
asthis
, the lambda is a free function. How does this come about? Doesconnect()
recognise this and have a different code path to implement? Doesstd::function()
(if that's whatconnect()
uses) handle this?@JonB There are two different connect overloads for member and non-member functions. In case of a member function connect stores the object and a function pointer. In case of non-member it's just a function pointer.
As tothis
- connect doesn't do anything special about it. It's easier to understand without connect.Take this example:
void SomeClass::someOtherFunction() { // Connect stores object and method for member functions SomeClass* object = this; void (SomeClass::* method)() = &SomeClass::someFunction; // And calls them like this when executed (object->*method)(); }
For free functions it's just:
// Store just the pointer void (*function)() = &someFunction; // And execute function();
Lambdas are (for the purpose of this exercise) just like free functions, except you can't specify the type, so
auto
:// Store just the pointer auto function = [](){}; // And execute function();
Since lambdas behave like functions you can't use
this
in them. Like in free functions there is no associated object.
If you want to use member functions inside the lambda you have to copy/reference the associated object in the catch clause.The very explicit version that names the captured object "Obj":
auto function = [Obj=this](){ qDebug() << Obj->objectName(); }
or you can just name it "this":
auto function = [this](){ qDebug() << this->objectName(); }
which is a shorthand that creates a variable named
this
and assigns it the value ofthis
from outer scope.
You can also capture it implicitly:auto function = [=](){ qDebug() << this->objectName(); }
The only special property of
this
inside a lambda, compared to any other variable, is that once you capture it you can skip naming it in the body, so:auto function = [=](){ qDebug() << objectName(); }
but that's just syntax sugar. Other than that it's just another regular variable copied in the capture clause.
AFAIK connect doesn't use std::function inside (it predates it), but an equivalent implementation, optimized for use with QObjects.
Also keep in mind that connect is very Qt specific, so it might not actually store member pointers, but something like a meta method id or something. I didn't look too deep at the implementation, but in any case it's something equivalent to function pointer, just optimized for given case.
-
Normally you can use a lambda where you could use a function/method and they work in essentially the same way as each other, they are interchangeable. There is something about
this
for the body of the lambda that I cannot quite get my ahead around.slotObject->setObjectName("slotObject"); connect(signalObject, &SignalClass::signalMethod, slotObject, &SlotClass::slotMethod); void SlotClass::slotMethod() { qDebug() << this->objectName(); }
slotObject
is thethis
in the body ofSlotClass::slotMethod()
---connect()
uses something likestd::function()
to set up to call asslotObject->slotMethod()
.Why is this not the case for a lambda slot?
connect(signalObject, &SignalClass::signalMethod, slotObject, []() { qDebug() << this->objectName(); } );
won't work (or compile) as-is. It is not doing some equivalent of
slotObject->lambda()
.While
&SlotClass::slotMethod
is a member method, and is called withslotObject
asthis
, the lambda is a free function. How does this come about? Doesconnect()
recognise this and have a different code path to implement? Doesstd::function()
(if that's whatconnect()
uses) handle this?@JonB Also, because I didn't mention it above,
slotObject
in case of free functions or lambdas is not used for executing them at all. It's just for scope of the connection, so that it disconnects when that object goes away, but it has nothing to do with the invoked function or lambda. It's not "passed" to the lambda (asthis
or otherwise) or anything like that. There's really no mechanism in C++ that would allow injecting it into the lambda after it is specified by the user. -
@JonB There are two different connect overloads for member and non-member functions. In case of a member function connect stores the object and a function pointer. In case of non-member it's just a function pointer.
As tothis
- connect doesn't do anything special about it. It's easier to understand without connect.Take this example:
void SomeClass::someOtherFunction() { // Connect stores object and method for member functions SomeClass* object = this; void (SomeClass::* method)() = &SomeClass::someFunction; // And calls them like this when executed (object->*method)(); }
For free functions it's just:
// Store just the pointer void (*function)() = &someFunction; // And execute function();
Lambdas are (for the purpose of this exercise) just like free functions, except you can't specify the type, so
auto
:// Store just the pointer auto function = [](){}; // And execute function();
Since lambdas behave like functions you can't use
this
in them. Like in free functions there is no associated object.
If you want to use member functions inside the lambda you have to copy/reference the associated object in the catch clause.The very explicit version that names the captured object "Obj":
auto function = [Obj=this](){ qDebug() << Obj->objectName(); }
or you can just name it "this":
auto function = [this](){ qDebug() << this->objectName(); }
which is a shorthand that creates a variable named
this
and assigns it the value ofthis
from outer scope.
You can also capture it implicitly:auto function = [=](){ qDebug() << this->objectName(); }
The only special property of
this
inside a lambda, compared to any other variable, is that once you capture it you can skip naming it in the body, so:auto function = [=](){ qDebug() << objectName(); }
but that's just syntax sugar. Other than that it's just another regular variable copied in the capture clause.
AFAIK connect doesn't use std::function inside (it predates it), but an equivalent implementation, optimized for use with QObjects.
Also keep in mind that connect is very Qt specific, so it might not actually store member pointers, but something like a meta method id or something. I didn't look too deep at the implementation, but in any case it's something equivalent to function pointer, just optimized for given case.
@Chris-Kawa said in `this` inside lambda body:
There are two different connect overloads for member and non-member functions
Yep, that would indeed explain.
All your examples with passing
[this]
or[=]
are not the same/what I was asking about. Those pass thethis
from the calling method, which obviously is quite different from theslotObject
which is in theconnect()
and which I was asking about being thethis
inside the lambda body.So summing up I think you are saying that a lambda is always a free function/functor, it never acts as a member function. You can explicitly pass in
[this]
or[=]
as context but there is something different about that. -
@Chris-Kawa said in `this` inside lambda body:
There are two different connect overloads for member and non-member functions
Yep, that would indeed explain.
All your examples with passing
[this]
or[=]
are not the same/what I was asking about. Those pass thethis
from the calling method, which obviously is quite different from theslotObject
which is in theconnect()
and which I was asking about being thethis
inside the lambda body.So summing up I think you are saying that a lambda is always a free function/functor, it never acts as a member function. You can explicitly pass in
[this]
or[=]
as context but there is something different about that.@JonB Yup, in case of lambdas the parameter would be better named
scopeObject
or something like that, as it's not directly related to the passed function/lambda. -
@JonB Also, because I didn't mention it above,
slotObject
in case of free functions or lambdas is not used for executing them at all. It's just for scope of the connection, so that it disconnects when that object goes away, but it has nothing to do with the invoked function or lambda. It's not "passed" to the lambda (asthis
or otherwise) or anything like that. There's really no mechanism in C++ that would allow injecting it into the lambda after it is specified by the user.@Chris-Kawa said in `this` inside lambda body:
Also, because I didn't mention it above, slotObject in case of free functions or lambdas is not used for executing them at all. It's just for scope of the connection, so that it disconnects when that object goes away, but it has nothing to do with the invoked function or lambda
Hmm, ISTM that is not true. In the case of a member method (not lambda), for
connect(signalObject, &SignalClass::signalMethod, slotObject, &SlotClass::slotMethod);
the code will effectively runslotObject->slotMethod()
. OIC, here you say "in case of free functions or lambdas", yes in those cases (instead of member method). That is what I was asking about, why it was different.So you can pass what you like for the context to a lambda, but you cannot make a lambda act as an implicit member method even if you want to, it's stuck as a free function, is that the case?
-
@Chris-Kawa said in `this` inside lambda body:
Also, because I didn't mention it above, slotObject in case of free functions or lambdas is not used for executing them at all. It's just for scope of the connection, so that it disconnects when that object goes away, but it has nothing to do with the invoked function or lambda
Hmm, ISTM that is not true. In the case of a member method (not lambda), for
connect(signalObject, &SignalClass::signalMethod, slotObject, &SlotClass::slotMethod);
the code will effectively runslotObject->slotMethod()
. OIC, here you say "in case of free functions or lambdas", yes in those cases (instead of member method). That is what I was asking about, why it was different.So you can pass what you like for the context to a lambda, but you cannot make a lambda act as an implicit member method even if you want to, it's stuck as a free function, is that the case?
@JonB said:
Hmm, ISTM that is not true.
Yes, I said the object is not related to free functions/lambdas. In case of member functions absolutely yes, it does double duty as the scope object and the object the member function is executed on.
you cannot make a lambda act as a member method even if you want to, it's stuck as a free function, is that the case?
Yes. Lambdas are a bit weird beasts. Like the dual nature of light they have a double identity - they behave like free functions and even decay to a free function pointer, but they are also like an unnamed class with () operator in that they store captured values in their scope, like class members. But they have no explicit type and they don't have their own
this
. When you invoke a lambda there is no hidden 'this' parameter passed like in case of class member functions.Unlike some other languages C++ doesn't allow you to modify the class at runtime, i.e. you can't tack on a random function to a class instance. That also goes for lambdas. You can't do
obj->someFunctionThatIsNotAMember()
orobj->someLambda
and have them getobj
asthis
. As always there are standard proposals though (I doubt they will ever get accepted, but who knows) :) -
@JonB said:
Hmm, ISTM that is not true.
Yes, I said the object is not related to free functions/lambdas. In case of member functions absolutely yes, it does double duty as the scope object and the object the member function is executed on.
you cannot make a lambda act as a member method even if you want to, it's stuck as a free function, is that the case?
Yes. Lambdas are a bit weird beasts. Like the dual nature of light they have a double identity - they behave like free functions and even decay to a free function pointer, but they are also like an unnamed class with () operator in that they store captured values in their scope, like class members. But they have no explicit type and they don't have their own
this
. When you invoke a lambda there is no hidden 'this' parameter passed like in case of class member functions.Unlike some other languages C++ doesn't allow you to modify the class at runtime, i.e. you can't tack on a random function to a class instance. That also goes for lambdas. You can't do
obj->someFunctionThatIsNotAMember()
orobj->someLambda
and have them getobj
asthis
. As always there are standard proposals though (I doubt they will ever get accepted, but who knows) :)@Chris-Kawa said in `this` inside lambda body:
That also goes for lambdas. You can't do
obj->someFunctionThatIsNotAMember()
orobj->someLambda
and have them getobj
asthis
. As always there are standard proposals though (I doubt they will ever get accepted, but who knows) :)Yep, that clarifies! I thought there might be a way that you could do that, but apparently not from C++.
Thanks for your responses.
-
J JonB has marked this topic as solved on