Ссылка на значение пустого опционала

Я видел следующий шаблон несколько раз:

// 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* вызовет неопределенное поведение (из-за нарушения предложения requires в [необязательный.observe]§5).

std::optional<T> storage;
T &t = *storage; // Looks okay, mines bitcoin when you're not looking

Возможно ли такое использование std::optional каким-то образом?
Если нет, то по какой причине его можно предотвратить?


person Quentin    schedule 26.12.2017    source источник
comment
Разве безопасное использование t в первом примере не требует предварительного запуска множества проверок, связанных с бухгалтерией?   -  person StoryTeller - Unslander Monica    schedule 26.12.2017
comment
@StoryTeller требует некоторой осторожности, но ничего, с чем не справится тире SBRM. Часто объект на самом деле имеет область действия main, и это просто о том, чтобы избежать слишком многого во время статической инициализации/уничтожения, сохраняя при этом глобальную точку доступа.   -  person Quentin    schedule 26.12.2017
comment
Я почти уверен, что T &t = reinterpret_cast<T &>(storage); демонстрирует неопределенное поведение, или, скорее, последующее использование t будет. Правильный способ сделать это T& t = *new(&storage) T;. По сути, это эквивалентно установке значения в std::optional. В итоге получается шесть одних, полдюжины других.   -  person Igor Tandetnik    schedule 26.12.2017
comment
@IgorTandetnik это должно быть эквивалентно жонглированию указателем здесь, что, по-видимому, нормально.   -  person Quentin    schedule 26.12.2017
comment
Из этого вопроса: Я использую новое размещение для создания объекта   -  person Igor Tandetnik    schedule 26.12.2017
comment
@IgorTandetnik ну и я тоже?   -  person Quentin    schedule 26.12.2017
comment
Ты? Насколько я могу судить, это нигде не фигурирует в вопросе. Я хочу сказать, что если вы это сделаете, то сможете в том же месте присвоить значение вашему optional, и тогда operator* будет работать.   -  person Igor Tandetnik    schedule 26.12.2017
comment
@IgorTandetnik упс, это было в демо-коде, но я забыл добавить его в сам вопрос. Это исправлено. Но здесь дело в том, чтобы получить указатель до того, как там будет построен объект.   -  person Quentin    schedule 26.12.2017


Ответы (1)


Это, в сочетании с адекватным пространством имен и именованием, обеспечивает приятный интерфейс (t) для пользователей переменной, в то же время позволяя отложенное построение, повторную инициализацию и т. д. фактического объекта на стороне библиотеки.

К сожалению, использование t для доступа к объекту, созданному позже по этому адресу, является поведением undefined. Это одна из причин почему предлагается std::launder.

Обратите внимание, что этот случай отличается от случая, описанного в этом вопросе. В этом вопросе ссылка/указатель получается после создания объекта типа T (хотя это также может быть неопределенным после C++17 без std::launder).

Возможно ли как-то такое использование std::Optional?

Как вы указали, это поведение undefined.

Если нет, то что может быть причиной для предотвращения этого?

Оптимизатор может обнаружить, что адрес связан с объектом, предоставляющим хранилище для T, и проигнорировать любой доступ к этому адресу через glvalue типа, вызывающий неопределенное поведение. На самом деле причины, по сути, заключаются в том, как строгие правила псевдонимов приносят пользу оптимизатору< /а>.

person xskxzr    schedule 26.12.2017
comment
Хм, жаль. Я полагаю, что std::launder не поможет, так как его нужно добавлять в каждой точке использования? - person Quentin; 26.12.2017
comment
@ Квентин Боюсь, что да. - person xskxzr; 26.12.2017