Enlico
Enlico

Reputation: 28396

When using either default capture mode, is this captured by copy or (*this) captured by reference? Is it the same thing?

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

  1. this is captured by value whether we use [=] or [&].
  2. (*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

Answers (2)

Davis Herring
Davis Herring

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

Nicol Bolas
Nicol Bolas

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

Related Questions