@Drottarutarnum
Web, Embedded and C#

Как передать в лямбду контекст класса?

C++14
Для простоты я убрал лишнее из класса
Дело в том, что лямбда совсем не меняет переменную test
Даже если я явно напишу this->test

Отладчик показал, что this внутри лямбды имеет другой адрес нежели сам экземпляр класса Protocol

Как быть?

#pragma once
#include <serial.h>

class Protocol {
private:
	Serial *serial;
	bool test = false;
    
public:
	Protocol (Serial &s) {
		serial = &s;
		serial->onInterrupt = [this]() {
			test = true;
		};
	}
};
  • Вопрос задан
  • 151 просмотр
Решения вопроса 1
@MarkusD Куратор тега C++
все время мелю чепуху :)
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.

В общем смысле, тебе стоит внимательнее смотреть за тем, какие операции ты выполняешь над объектами, сырой указатель на которые ты хочешь где-то сохранить.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
Чтобы подобное не повторилось в будущем, нужно удалить конструкторы копирования/перемещения и операторы копирования/перемещения у Protocol. Этот класс действительно должен быть non-movable, т.к. его адрес сохраняется в лямбде, которую тяжело контролировать.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы
Тages Санкт-Петербург
от 100 000 до 200 000 ₽
Тages Санкт-Петербург
от 50 000 до 80 000 ₽
Тages Санкт-Петербург
от 100 000 до 200 000 ₽