Поскольку опциональная цель состоит в том, чтобы позволить нам использовать объекты с формальным неинициализированным дополнительным состоянием, интерфейс может попытаться максимально следовать интерфейсу базового типа<T
>. Чтобы выбрать правильную степень принятия нативного интерфейса<T
>, необходимо отметить следующее: Даже если все операции, поддерживаемые экземпляром типа<T
>, определены для всего диапазона значений для такого типа,<optional<T>
>расширяет такой набор значений новым значением, для которого большинство (иначе действительных) операций не определены в терминах<T
>.
Кроме того, поскольку<optional<T>
>сама по себе является всего лишь<T
>оберткой (моделирование<T
>супертипа), любая попытка определить такие операции по неинициализированным опциональным вариантам будет полностью искусственной.<T
>.
Эта библиотека выбирает интерфейс, который следует из интерфейса<T
>только для тех операций, которые хорошо определены (w.r.t типа<T
>), даже если любой из операндов не инициализирован. Эти операции включают в себя: строительство, копирование, присвоение, обмен и реляционные операции.
Для операций доступа к значениям, которые не определены (w.r.t тип<T
>), когда операнд неинициализирован, выбирается другой интерфейс (который будет объяснен далее).
Также наличие возможно неинициализированного состояния требует дополнительных операций, не предусмотренных самим<T
>, которые поддерживаются специальным интерфейсом.
Особенностью указателя является то, что он может иметьнулевое значение указателя. Этоспециальноезначение, которое используется для указания на то, что указатель вообще не относится к какому-либо объекту. Другими словами, значения нулевых указателей передают понятие несуществующих объектов.
Это значение значения нулевого указателя позволило указателям стать стандартомде-фактодля обработки необязательных объектов, потому что все, что вам нужно сделать, чтобы обратиться к значению, которого у вас на самом деле нет, это использовать нулевое значение указателя соответствующего типа. Указатели использовались в течение десятилетий— со времен C APIs до современных библиотек C++— додля обозначения факультативных (то есть, возможно, несуществующих) объектов; особенно в качестве дополнительных аргументов для функции, но также довольно часто в качестве факультативных членов данных.
Возможное присутствие нулевого значения указателя делает операции, которые получают доступ к значению указателя, возможно, неопределенными, поэтому выражения, которые используют операторы отсчета и доступа, такие как:<(
*p
=2)
>и<(
p->foo())
>, неявно передают понятие факультативности, и эта информация связана с синтаксисомвыражений. То есть наличие операторов<*
>и<->
>говорят сами по себе — без какого-либо дополнительного контекста— что выражение будет неопределенным, если подразумеваемый указатель фактически не существует.
Такаяде-фактоидиома для обозначения факультативных объектов может быть формализована в виде понятия:<OptionalPointee
>понятие. Эта концепция фиксирует синтаксическое использование операторов<*
>,<->
>и контекстное преобразование в<bool
>для передачи понятия факультативности.
Однако указатели хороши дляссылкина необязательные объекты, но не особенно хороши для обработки необязательных объектов во всех других отношениях, таких как инициализация или перемещение / копирование их. Проблема заключается в мелкой копии семантики указателей: если вам нужно эффективно перемещать или копировать объект, одних указателей недостаточно. Проблема в том, что копии указателей не подразумевают копии указателей. Например, как обсуждалось в мотивации, указатели сами по себе не могут быть использованы для возврата необязательных объектов из функции, потому что объект должен перемещаться из функции в контекст вызывающего.
Решение проблемы мелкой копии, которая часто используется, заключается в том, чтобы прибегнуть к динамическому распределению и использовать интеллектуальный указатель для автоматического управления деталями этого. Например, если функция необязательно возвращает объект<X
>, она может использовать<shared_ptr<X>
>в качестве возвращаемого значения. Однако это требует динамического распределения<X
>. Если<X
>— это встроенный или небольшой POD, то этот метод очень беден с точки зрения требуемых ресурсов. Факультативные объекты по существу являются значениями, поэтому очень удобно использовать автоматическое хранение и глубококопирующую семантику для манипулирования дополнительными значениями, как мы делаем с обычными значениями. Указатели не имеют этой семантики, поэтому не подходят для инициализации и переноса необязательных значений, но довольно удобны для обработки доступа к возможному неопределенному значению из-за идиоматической помощи, присутствующей в концепции<OptionalPointee
>, воплощенной указателями.
Для операций доступа к значениям<optional<>
>операторы<*
>и<->
>лексически предупреждают о возможно неинициализированном состоянии, апеллируя к знакомой семантике указателей w.r.t. к нулевым указателям.
![[Caution]](/img/caution.png) |
Caution |
Однако особенно важно отметить, что<optional<> >объекты не являются указателями.<optional<> >не является и не является образцом. |
Например,<optional<>
>не имеет мелкой копии, поэтому не является псевдонимом: два различных опциональных значения никогда не относятся ктому жезначению, если<T
>сам по себе не является ссылкой (но может иметьэквивалентзначения). Следует иметь в виду разницу между<optional<T>
>и указателем, особенно потому, что семантика реляционных операторов различна: поскольку<optional<T>
>является оберткой значений, реляционные операторы глубоки: они сравнивают необязательные значения; но реляционные операторы для указателей неглубоки: они не сравнивают значения указателей. В результате вы можете заменить<optional<T>
>на<T*
>в некоторых ситуациях, но не всегда. В частности, на общем коде, написанном для обоих, вы не можете использовать реляционные операторы напрямую и должны использовать функции шаблона<equal_pointees()
>и<less_pointees()
>вместо этого.