2019-07-09

C++20 Deprecate implicit lambda capture of this

C++20 deprecated the implicit lambda capture of this. What does it mean and how can we keep up with the change?

As of now, you probably don't need to do anything. Because it's just the deprecation, not the removal. But in the future, it will eventually be removed so be prepared.

So what is "implicit lambda capture of this" anyway? The following code relies on that behavior.

struct S
{
    int x ;
    void f()
    {
        // C++17: Well-formed
        // C++20: Deprecated.
        [=]{ std::cout << x ; }
    }
} ;

So above code use the variable x. this name x is a data member of class S. So "x" in this context means "this->x". Above code works because lambda capture implicitly capture this pointer. That's why we can use "x" inside the lambda expression.

You may not realize it but this "x" is not captured by copy. In fact, "x" is not captured at all. What lambda capture instead is this pointer. So we can use "x" via the this pointer. So "x" is effectively captured by reference. We can change the value of x.


void f()
[
    auto change_x = [=]( auto value ) { x = value ; } ;
    change_x(123) ;
}

This behavior breaks novice's assumption that lambda capture clause "[=]" means capture by value. People may write code like this:

struct S
{
    int x ;
    auto get_closure()
    {
        return [=]{ return x ; } ;
    }
} ;

int main()
{
    std::function<int()> f ;
    {
        S s{123} ;
        f = s.get_closure() ;
        // s's lifetime ends here
    }
    int result = f() ;
}

Assuming that it's safe because of captured by value and get caught by Undefined Behavior.

C++17 partially fixed this by introducing the explicit capture of this.


struct S
{
    int x ;
    void f()
    {
        // capture this pointer by value, x is reference
        [this]{ x ; } ;
        // catpure *this by value, x means x is value
        [*this] { x ; } ;
    }
} ;

"[*this]" copy the this pointer which means x is accessed through this pointer. "[*this}" copy the *this, that is the value of class object S. Since it copy the class object, x is copied too.

C++17 retained the implicit capture of this.

In C++20, it is decided that the standard deprecate the implicit capture of this via [=].


struct S
{
    int x ;
    void f()
    {
        // C++17: well-formed, it means [=, this]
        // C++20: deprecated, it still means [=, this]
        // C++??: if removed, ill-formed.
        [=] { x ; } ;
    }
} ;

The behavior of "[&]" doesn't change. It still implicitly capture this pointer by value. So the data members are effectively captured by reference. There should be no confusion on this.

1 comment:

Anonymous said...

> "[*this]" copy the this pointer which means x is accessed through this pointer. "[*this}" copy the *this, that is the value of class object S. Since it copy the class object, x is copied too.
s/[*this]/[this]/
s/[*this}/[*this]/