TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.35K subscribers
Post #787 4.67K
История capture this
#опытным

Проследим историю захвата this в лямбду сквозь стандарты С++, там есть на что посмотреть.

С++11

Появились лямбды и в них можно захватывать this как явно, так и неявно через параметры захвата по-умолчанию. Во всех случаях захватывается &(*this) то есть указатель на текущий объект:

struct Foo {
int m_x = 0;

void func() {
int x = 0;

//Explicit capture 'this'
[this]() { /*access m_x and x*/ }();

//Implcit capture 'this'
[&]() { /*access m_x and x*/ }();

//Redundant 'this'
[&, this]() { /*access m_x and x*/ }();

//Implcit capture 'this'
[=]() { /*access m_x and x*/ }();

//Error
[=, this]() { }();
}
};


Однако не было адекватного способа захватить объект по значению aka скопировать его в лямбду.

С++14

Появилась инициализация в захвате, поэтому стал реальным захват объекта по значению:

struct Foo {
int m_x = 0;

void func() {
[copy=*this]() mutable {
copy.m_x++;
}();
}
};


В остальном все осталось также.

С++17

Появился захват объекта по значению! Захват по значению может быть очень важно для асинхронных операций, которые откладывают выполнение лямбд:

struct Processor {
//Some state data..

std::future<void> process(/*args*/) {
//Pre-process...
//Do the data processing asynchronously
return
std::async(std::launch::async,
[=](/*data*/){
/*
Runs in a different thread.
'this' might be invalidated here
*/

//process data
});
}
};

auto caller() {
Processor p;
return p.process(/*args*/);
}


В этом коде UB, потому что возвращаем лямбду со ссылкой на несуществующий объект. Поменять это можно захватом объекта по значению:

struct Processor {
std::future<void> process(/*args*/) {
return
std::async(std::launch::async,
[*this](/*data*/){
/*
Runs in a different thread.
'this' might be invalidated here
*/

//process data
});
}
};

В этом случае в лямбде будет храниться копия объекта и все методы будут обращаться к этому скопированному объекту.

С++20

С появлением захвата this по значению стала очень путающей семантика неявного захвата. Что reference capture &, что value capture =, по факту захватывали текущий объект по ссылке. И ничто неявно не захватывало this по значению.

Изначально в 20-м стандарте эту проблему хотели решить, просто запретив неявный захват this в любом случае. Но посидели и поняли, что для ссылочного захвата по умолчанию семантика неявного захвата ссылки на объект(чем отдаленно явняется this) корректна. А вот для захвата по значению - нет.

Поэтому начиная с С++20 мы не можем неявно захватывать this в default capture by value:

struct Bagel {
int x = 0;
void func() {
//OK until C++20. Warning in C++20.
[=]() { std::cout << x; }();

//Error/warning until C++20. OK in C++20.
[=, this]() { std::cout << x; }();
}
};


Оставлю в картинке под постом инфографику по изменениям в стандартах относительно захвата this.

Know the history. Stay cool.

#cpp11 #cpp14 #cpp17 #cpp20
  • 🔥 16
  • 👍 11
  • ❤ 10
  • 🤯 7
More from @grokaemcpp
  1. Oct 12, 2026​​WAT #новичкам Как вы думаете, результат выражения: bool math = -1 < 1U; будет правдой ил…
  2. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  3. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  4. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  5. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
  6. Sep 28, 2026​​Stacktrace #опытным Одна из проблема исключений - непонятно, откуда оно прилетело. Ну да…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →