TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.35K subscribers
Post #673 5.16K
Mutable lambdas
#опытным

Лямбда выражения имеют одну интересную особенность. И эта особенность аффектит то, что можно делать внутри лямбды.

Простой пример:
int val = 0;
auto lambda1 = [&val]() { std::cout << ++val << std::endl; };
auto lambda2 = [val]() { std::cout << ++val << std::endl; };


Определяем 2 лямбды: в одну захватываем val по ссылке, во второй - по значению.

В чем здесь проблема?

А в том, что во втором случае мы получим ошибку компиляции.

На самом деле operator() у замыкания по умолчанию помечен как const метод, видимо чтобы его можно было вызывать на константных объектах замыкания. То есть это значит, что мы не можем изменять поля замыкания при вызове лямбды.

Ссылки интересным образом это ограничение обходят. Так как ссылки сами по себе неизменяемы(так как по факту это обертка над константным указателем), то формально требования выполняются. А то, что мы изменяем объект, на который указывает ссылка - "вы не понимаете, это другое".

Под одним из прошлых постов разгорелась дискуссия по этому моменту. @KpacHoe_ympo в этом комменте упомянул, что в константных методах можно менять объекты, на которые ссылаются ссылки. Однако на мой вгляд(и подтверждений в стандарте я не нашел), что это не уб. Иначе в лямбду нельзя было бы захватывать ссылки вообще. Вряд ли весь захват по ссылке в лямбду держится на уб.

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

Но если нам очень нужно изменять захваченные по значению поля? На помощь приходит уже полюбившийся нам mutable. Лямбду можно пометить этим ключевым словом и тогда ее константный оператор() перестанет быть константным! Тогда мы можем как угодно изменять любые захваченные значения:

int val = 0;
auto lambda2 = [val]() mutable { std::cout << ++val << std::endl; };


Теперь все работает отлично.

То есть в лямбда выражениях mutable используется в случаях, когда необходима модификация объектов, захваченных по значению.

Это может использоваться, например, для перемещения захваченных объектов в one-shot коллбэках:

auto callback = [message=get_message, &scheduler]() mutable {
// some preparetions
scheduler.submit(std::move(message));
}
SomeTask task{callback};
task.run();


По завершению таски, коллюэк кладет в шедулер мувнутое сообщения без накладных расходов на копирование.

Мутабельные лямбды - не такая популярная фича, еще менее известная, чем обычный mutable, но о их существовании нужно знать.

Break the rules. Stay cool.

#cppcore
  • ❤ 23
  • 👍 15
  • 🔥 12
  • 💯 2
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 →