Drottarutarnum,
p = { s };
- это
Copy Initialization.
В буквальном смысле, что тут происходит. Сперва ты создаешь
temporary local через конструктор
Protocol::Protocol(Serial &s)
. Следом, через
operator =
, который сводится к конструктору копирования/перемещения, ты делаешь
Copy Construction объекта
p
. Делается это через конструктор копирования/перемещения по умолчанию, который просто копирует/перемещает поля используя их конструкторы копирования/перемещения.
Лямбда создается внутри temporary local значения, т.к. именно для него вызывается соответствующий конструктор. Далее эта лямбда остается жить в объекте
Serial
, а сам temporary local уничтожается сразу же после конструирования
p
.
После этого в замыкании лямбды остается
dangling pointer на уже уничтоженный
this
.
Одним из возможных решений данной проблемы может быть не
p = { s };
, а
p{ s };
или, как ты уже описал,
p = s;
. Такой тип инициализации уже называется
Value Initialization. Но по своей сути это только отсрочка проблемы. Тебе придется очень пристально следить за жизнью такого объекта, т.к. вероятность обрести все тот же висячий указатель в замыкании в следствии неявного копирования крайне высока.
Другим из возможных решений такой проблемы может быть использование
std::enable_shared_from_this
[
?],
std::shared_ptr
[
?] для хранения протокола и
std::weak_ptr
[
?] в замыкании твоей лямбды. Этот метод гарантированно решит твою проблему, но может привести к ряду других
проблем с возможной утечкой ресурсов.
В качестве еще одного способа - научить
Serial
отцеплять лямбду уничтожаемого
Protocol
и написать правильный конструктор копирования
Protocol
, соблюдая
правило 3/5/0.
В общем смысле, тебе стоит внимательнее смотреть за тем, какие операции ты выполняешь над объектами, сырой указатель на которые ты хочешь где-то сохранить.