Home > front end >  C 11 lambda: when do we need to capture [*this] instead of [this]?
C 11 lambda: when do we need to capture [*this] instead of [this]?

Time:08-02

For a quick sample we could see:

class Foo {
  std::string s_; 
  int i_;
public:
  Foo(const std::string& s, int i) : s_(s), i_(i) {}
  void Print() {
    auto do_print = [this](){
      std::cout << s_ << std::endl;
      std::cout << i_ << std::endl;
    };
    do_print();
  }
};

Ok, capture [this], so the lambda do_print could use s_ and i_ member of Foo. Capture [this] is enough.

But when do we have to capture [*this] instand of [this]? What could be a typical use-case / scenario, any quick samples?

Thanks!

CodePudding user response:

You capture *this if you want a copy of the element to be captured. This can be useful if the element may change or go out of scope after the lambda is created, but you want an element that is as it was at the time of creating the lambda.

#include <iostream>

class Foo {
    std::string s_;
    int i_;

public:
    Foo(const std::string& s, int i) : s_(s), i_(i) {}
    auto get_print() {
        return [*this] {                    // copy
            std::cout << s_ << std::endl;
            std::cout << i_ << std::endl;
        };
    }
};

auto func() {
    Foo foo("foo", 123);
    return foo.get_print();
} // foo goes out of scope

int main() {
    func()(); // but the lambda holds a copy, so this is ok, otherwise, UB
}

Also note that capturing *this requires C 17. A similar program in C 11 could look like this:

#include <functional>
#include <iostream>

class Foo {
    std::string s_;
    int i_;

public:
    Foo(const std::string& s, int i) : s_(s), i_(i) {}

    std::function<void()> get_print() {
        auto Self = *this;
        return [Self] {
            std::cout << Self.s_ << std::endl;
            std::cout << Self.i_ << std::endl;
        };
    }
};

std::function<void()> func() {
    Foo foo("foo", 123);
    return foo.get_print();
}

int main() {
    func()();
}

CodePudding user response:

The general rule with lambdas is that if the lambda doesn't escape the current scope (including copies), you can capture with [this] or [&] safely.

If it does escape the current scope, you should default to [=] and [*this] (well, and also naming things for [=]). You should only capture by reference when the lambda escapes the current scope if you are willing to personally guarantee the lifetime of everything capture now and forever in the future, whenever the code is modified by a university intern just before they go back to school for their first course on C programming.

Lifetime management is hard. You should avoid doing it and just capture things by value whenever you can.

  • Related