Reputation: 28396
I was a bit puzzled when I saw the following working
struct A {
void g(){}
void f(){
[&](){ g(); }();
}
};
but then I found this
answer which explains in quite deep detail how that works.
In essence, it boils down to this
is captured by value whether you use [=]
or [&]
.
However, on cppreference I read the following
The current object
(*this)
can be implicitly captured if either capture default is present. If implicitly captured, it is always captured by reference, even if the capture default is=
.
So I have two formulations which seem to be equivalent
this
is captured by value whether we use [=]
or [&]
.(*this)
is captured by reference whether we use [=]
or [&]
.First of all, are indeed the two formulations above perfectly equivalent?
I do see a difference between the two, if not in what's practically possibile, at least in terms how useful they look.
Formulation 1 seem just weird to me. Why preventing me from capturing this
by const
-reference? Performance-wise there shouldn't be any big difference in the two cases, and I can always do auto const& This = this;
before the lambda, and then capture This
by reference, if I want to. As regards dangling references, if I'm using the this
keyword, I'm in the object class, so how can the object die while I'm inside it?
Formulation 2, on the other hand, does seem to protect me from making unnecessary copies that would happen if (*this)
was captured by value when using [=]
. Furthermore, if I really want to, there's a capture syntax for capturing (*this)
by copy, namely [*this]
.
Upvotes: 3
Views: 543
Reputation: 39788
The first interpretation was the intent prior to C++17 (when that answer was written), but the second is the intent since (as reflected by the ever-current cppreference). Originally it was considered that this
(the pointer) was the subject of the capture, and it is of course impossible to do so by reference (as pointed out; const auto &x=…;
is not at all the same thing if it introduces a temporary). C++17 introduced capturing *this
(by value), reinterpreting the existing capture [this]
as being effectively [&*this]
(which is not actually valid syntax).
C++20 continues the transition, deprecating
struct A {
auto f() {return [=] {return this;}}
};
since it doesn't actually capture anything by value, and conversely allowing [=,this]
which is now taken to mean "capture everything by value except for *this
" rather than the redundant "capture everything by value and also this
by value". Conversely, [&,this]
should now be considered to be redundant, but it hasn't been deprecated (yet).
Upvotes: 3
Reputation: 473282
First of all, are indeed the two formulations above perfectly equivalent?
You can think of them as functionally being the same thing, but the second one is not really the best way to think of it. this
is always captured by value. But sine this
is a pointer, the value being captured is just the pointer to that object. So you can conceptually think of it as capturing the object being pointed to by reference through a pointer.
Why preventing me from capturing this by reference?
Because the expression this
is a prvalue. There is no object called this
which can be referenced. That's why if you do auto& This = this;
, you get a compile error.
Remember: this
is a pointer to an object. The pointer is the prvalue; the object being pointed to is not.
As regards dangling references, if I'm using the this keyword, I'm in the object class, so how can the object die while I'm inside it?
Besides the fact that you can call this->~Typename()
to destroy the object, a lambda is just a callable object. It can persist beyond the boundaries of the location that created it. It can be returned (either via type-erasure like through a std::function
or by auto
return type deduction) or it can be passed to someone else and stored beyond the scope of the object.
Upvotes: 2