При сбросе std::optional с содержащимся объектом что происходит с указателем, ранее полученным на этот объект?
После сброса std::optional содержащийся объект уничтожается, поэтому ранее полученный указатель становится недействительным. Сам объект std::optional продолжает существовать, но разыменование такого указателя после сброса приводит к неопределённому поведению; после нового помещения объекта указатель следует получить заново.
std::optional появился в стандартной библиотеке C++17 как тип для представления значения, которое может присутствовать или отсутствовать. Он решает задачу хранения объекта непосредственно внутри оболочки без обязательного динамического выделения памяти и без использования специальных указателей вроде null.
Такой подход отделяет время жизни оболочки от времени жизни содержащегося объекта. Оболочка может существовать постоянно, тогда как её значение создаётся и уничтожается по мере необходимости.
Вызов reset() у std::optional завершает время жизни содержащегося объекта, если он присутствует. Указатель или ссылка, полученные до этого, не продлевают время жизни объекта и после уничтожения становятся висячими.
Ошибочная логика часто возникает из-за того, что память самого std::optional не освобождается. Однако сохранение адреса памяти не означает сохранение времени жизни объекта: после reset() по этому адресу больше нет прежнего объекта нужного типа.
При наличии значения std::optional<T> содержит объект T. Вызов reset() вызывает деструктор этого объекта и переводит оболочку в состояние «пусто». Деструктор самого std::optional при этом не вызывается, а занимаемое оболочкой хранилище может остаться тем же.
Указатель не владеет объектом и не влияет на его время жизни. После уничтожения T указатель хранит прежнее значение адреса, но разыменование этого значения не даёт доступ к живому объекту и является неопределённым поведением.
Если затем вызвать emplace(), в хранилище будет создан новый объект. Даже если он окажется по тому же адресу и будет иметь тот же тип, безопасная практика — получить указатель или ссылку заново. Нельзя использовать старый указатель как средство отслеживания последующих объектов в optional.
Это отличается от std::unique_ptr: у unique_ptr сброс уничтожает динамический объект и освобождает его ресурс, тогда как optional управляет временем жизни объекта внутри собственной оболочки. В обоих случаях старый указатель после уничтожения объекта использовать нельзя.
Кэш хранит необязательный объект конфигурации, а другой компонент временно сохраняет указатель на его поля. При обновлении конфигурации кэш вызывает reset(), а затем создаёт новую конфигурацию через emplace(). Сохранённый указатель после этого может указывать на уничтоженный объект.
Возможны три варианта:
optional;optional — лучше связывает доступ с актуальным временем жизни, но требует, чтобы владелец оставался доступным.Рациональное решение — не кэшировать указатель через операции, которые могут вызвать reset() или emplace(). Компонент должен обращаться к optional через владельца непосредственно перед использованием либо получать новый указатель после каждого изменения состояния. Это устраняет неопределённое поведение и делает границы валидности явными.
Продлевает ли указатель или ссылка время жизни объекта внутри std::optional?
Нет. Ни сырой указатель, ни ссылка, ни std::reference_wrapper не являются владельцами и не меняют время жизни содержащегося объекта. Объект живёт только пока optional находится в состоянии, содержащем значение, и пока не выполнена операция, уничтожающая это значение.
Освобождает ли reset() память, занятую самим std::optional?
Не обязательно и обычно не в том смысле, который применим к динамической памяти. reset() уничтожает объект T, но оболочка optional продолжает существовать; её размер и место размещения не меняются. Если T сам владеет ресурсом через RAII, его деструктор освободит этот ресурс, но это не означает уничтожения оболочки.
Можно ли использовать старый указатель после повторного emplace(), если новый объект создан по тому же адресу?
Рассчитывать на это нельзя. После reset() старый объект уничтожен, а после emplace() появляется новый объект; правила повторного использования указателей зависят от условий замены объектов и особенностей вложенного хранилища. Надёжное решение — заново получить указатель или ссылку из optional после emplace(), не полагаясь на совпадение адресов.