Ссылка на значение пустого необязательного
Я видел следующую картину несколько раз:
// T is a type, this is at namespace scope
std::aligned_storage_t<sizeof(T), alignof(T)> storage;
T &t = reinterpret_cast<T &>(storage);
Это, в сочетании с адекватным пространством имен и имен, обеспечивает приятный интерфейс (t
) для пользователей переменной, обеспечивая возможность отложенного построения, повторной инициализации и т. д. реального объекта на стороне библиотеки при размещении new
и явные вызовы деструкторов. Вы можете видеть это работает здесь.
Сейчас, std::aligned_storage
и все, но C++17 дал нам новый инструмент для такого разделения времени хранения объекта хранения, то есть std::optional
,
Тем не менее, два способа доступа к значению std::optional
(value()
а также operator*
) оба требуют значения, чтобы действительно быть там; иначе value()
будет бросать std::bad_optional_access
, в то время как operator*
вызовет неопределенное поведение (за нарушение условия require в [option.observe] §5).
std::optional<T> storage;
T &t = *storage; // Looks okay, mines bitcoin when you're not looking
Такое использование std::optional
все еще возможно как-то?
Если нет, что было бы причиной для предотвращения этого?
1 ответ
Это, в сочетании с адекватным пространством имен и именами, обеспечивает приятный интерфейс (t) для пользователей переменной, в то же время обеспечивая отложенное создание, повторную инициализацию и т. Д. Реального объекта на стороне библиотеки.
К сожалению, используя t
доступ к объекту, созданному позже по этому адресу, является неопределенным поведением. Это одна из причин, почему std::launder
предлагается.
Обратите внимание, что этот случай отличается от случая, описанного в этом вопросе. В этом вопросе ссылка / указатель получается после объекта типа T
создается (хотя это также может быть неопределенным после C++17 без std::launder
).
Такое использование std::option все еще возможно?
Как вы указали, это неопределенное поведение.
Если нет, что было бы причиной для предотвращения этого?
Оптимизатор может обнаружить, что адрес связан с объектом, который предоставляет хранилище для T
и игнорировать любой доступ к этому адресу через glvalue типа, который вызывает неопределенное поведение. На самом деле, причина в том, что правила строгого наложения выгодны для оптимизатора.