123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- [/
- Boost.Optional
- Copyright (c) 2003-2007 Fernando Luis Cacciola Carballal
- Distributed under the Boost Software License, Version 1.0.
- (See accompanying file LICENSE_1_0.txt or copy at
- http://www.boost.org/LICENSE_1_0.txt)
- ]
- [section Design Overview]
- [section The models]
- In C++, we can ['declare] an object (a variable) of type `T`, and we can give this
- variable an ['initial value] (through an ['initializer]. (cf. 8.5)).
- When a declaration includes a non-empty initializer (an initial value is given),
- it is said that the object has been initialized.
- If the declaration uses an empty initializer (no initial value is given), and
- neither default nor value initialization applies, it is said that the object is
- [*uninitialized]. Its actual value exist but has an ['indeterminate initial value]
- (cf. 8.5/11).
- `optional<T>` intends to formalize the notion of initialization (or lack of it)
- allowing a program to test whether an object has been initialized and stating
- that access to the value of an uninitialized object is undefined behavior. That
- is, when a variable is declared as `optional<T>` and no initial value is given,
- the variable is ['formally] uninitialized. A formally uninitialized optional object
- has conceptually no value at all and this situation can be tested at runtime. It
- is formally ['undefined behavior] to try to access the value of an uninitialized
- optional. An uninitialized optional can be assigned a value, in which case its initialization state changes to initialized. Furthermore, given the formal
- treatment of initialization states in optional objects, it is even possible to
- reset an optional to ['uninitialized].
- In C++ there is no formal notion of uninitialized objects, which means that
- objects always have an initial value even if indeterminate.
- As discussed on the previous section, this has a drawback because you need
- additional information to tell if an object has been effectively initialized.
- One of the typical ways in which this has been historically dealt with is via
- a special value: `EOF`, `npos`, -1, etc... This is equivalent to adding the
- special value to the set of possible values of a given type. This super set of
- `T` plus some ['nil_t]—where `nil_t` is some stateless POD—can be modeled in modern
- languages as a [*discriminated union] of T and nil_t. Discriminated unions are
- often called ['variants]. A variant has a ['current type], which in our case is either
- `T` or `nil_t`.
- Using the __BOOST_VARIANT__ library, this model can be implemented in terms of `boost::variant<T,nil_t>`.
- There is precedent for a discriminated union as a model for an optional value:
- the __HASKELL__ [*Maybe] built-in type constructor. Thus, a discriminated union
- `T+nil_t` serves as a conceptual foundation.
- A `variant<T,nil_t>` follows naturally from the traditional idiom of extending
- the range of possible values adding an additional sentinel value with the
- special meaning of ['Nothing]. However, this additional ['Nothing] value is largely
- irrelevant for our purpose since our goal is to formalize the notion of
- uninitialized objects and, while a special extended value can be used to convey
- that meaning, it is not strictly necessary in order to do so.
- The observation made in the last paragraph about the irrelevant nature of the
- additional `nil_t` with respect to [_purpose] of `optional<T>` suggests an
- alternative model: a ['container] that either has a value of `T` or nothing.
- As of this writing I don't know of any precedent for a variable-size
- fixed-capacity (of 1) stack-based container model for optional values, yet I
- believe this is the consequence of the lack of practical implementations of
- such a container rather than an inherent shortcoming of the container model.
- In any event, both the discriminated-union or the single-element container
- models serve as a conceptual ground for a class representing optional—i.e.
- possibly uninitialized—objects.
- For instance, these models show the ['exact] semantics required for a wrapper
- of optional values:
- Discriminated-union:
- * [*deep-copy] semantics: copies of the variant implies copies of the value.
- * [*deep-relational] semantics: comparisons between variants matches both
- current types and values
- * If the variant's current type is `T`, it is modeling an ['initialized] optional.
- * If the variant's current type is not `T`, it is modeling an ['uninitialized]
- optional.
- * Testing if the variant's current type is `T` models testing if the optional
- is initialized
- * Trying to extract a `T` from a variant when its current type is not `T`, models
- the undefined behavior of trying to access the value of an uninitialized optional
- Single-element container:
- * [*deep-copy] semantics: copies of the container implies copies of the value.
- * [*deep-relational] semantics: comparisons between containers compare container
- size and if match, contained value
- * If the container is not empty (contains an object of type `T`), it is modeling
- an ['initialized] optional.
- * If the container is empty, it is modeling an ['uninitialized] optional.
- * Testing if the container is empty models testing if the optional is
- initialized
- * Trying to extract a `T` from an empty container models the undefined behavior
- of trying to access the value of an uninitialized optional
- [endsect]
- [section The semantics]
- Objects of type `optional<T>` are intended to be used in places where objects of
- type `T` would but which might be uninitialized. Hence, `optional<T>`'s purpose is
- to formalize the additional possibly uninitialized state.
- From the perspective of this role, `optional<T>` can have the same operational
- semantics of `T` plus the additional semantics corresponding to this special
- state.
- As such, `optional<T>` could be thought of as a ['supertype] of `T`. Of course, we
- can't do that in C++, so we need to compose the desired semantics using a
- different mechanism.
- Doing it the other way around, that is, making `optional<T>` a ['subtype] of `T`
- is not only conceptually wrong but also impractical: it is not allowed to
- derive from a non-class type, such as a built-in type.
- We can draw from the purpose of `optional<T>` the required basic semantics:
- * [*Default Construction:] To introduce a formally uninitialized wrapped
- object.
- * [*Direct Value Construction via copy:] To introduce a formally initialized
- wrapped object whose value is obtained as a copy of some object.
- * [*Deep Copy Construction:] To obtain a new yet equivalent wrapped object.
- * [*Direct Value Assignment (upon initialized):] To assign a value to the
- wrapped object.
- * [*Direct Value Assignment (upon uninitialized):] To initialize the wrapped
- object with a value obtained as a copy of some object.
- * [*Assignment (upon initialized):] To assign to the wrapped object the value
- of another wrapped object.
- * [*Assignment (upon uninitialized):] To initialize the wrapped object with
- value of another wrapped object.
- * [*Deep Relational Operations (when supported by the type T):] To compare
- wrapped object values taking into account the presence of uninitialized states.
- * [*Value access:] To unwrap the wrapped object.
- * [*Initialization state query:] To determine if the object is formally
- initialized or not.
- * [*Swap:] To exchange wrapped objects. (with whatever exception safety
- guarantees are provided by `T`'s swap).
- * [*De-initialization:] To release the wrapped object (if any) and leave the
- wrapper in the uninitialized state.
- Additional operations are useful, such as converting constructors and
- converting assignments, in-place construction and assignment, and safe
- value access via a pointer to the wrapped object or null.
- [endsect]
- [section The Interface]
- Since the purpose of optional is to allow us to use objects with a formal
- uninitialized additional state, the interface could try to follow the
- interface of the underlying `T` type as much as possible. In order to choose
- the proper degree of adoption of the native `T` interface, the following must
- be noted: Even if all the operations supported by an instance of type `T` are
- defined for the entire range of values for such a type, an `optional<T>`
- extends such a set of values with a new value for which most
- (otherwise valid) operations are not defined in terms of `T`.
- Furthermore, since `optional<T>` itself is merely a `T` wrapper (modeling a `T`
- supertype), any attempt to define such operations upon uninitialized optionals
- will be totally artificial w.r.t. `T`.
- This library chooses an interface which follows from `T`'s interface only for
- those operations which are well defined (w.r.t the type `T`) even if any of the
- operands are uninitialized. These operations include: construction,
- copy-construction, assignment, swap and relational operations.
- For the value access operations, which are undefined (w.r.t the type `T`) when
- the operand is uninitialized, a different interface is chosen (which will be
- explained next).
- Also, the presence of the possibly uninitialized state requires additional
- operations not provided by `T` itself which are supported by a special interface.
- [heading Lexically-hinted Value Access in the presence of possibly
- uninitialized optional objects: The operators * and ->]
- A relevant feature of a pointer is that it can have a [*null pointer value].
- This is a ['special] value which is used to indicate that the pointer is not
- referring to any object at all. In other words, null pointer values convey
- the notion of nonexistent objects.
- This meaning of the null pointer value allowed pointers to became a ['de
- facto] standard for handling optional objects because all you have to do
- to refer to a value which you don't really have is to use a null pointer
- value of the appropriate type. Pointers have been used for decades—from
- the days of C APIs to modern C++ libraries—to ['refer] to optional (that is,
- possibly nonexistent) objects; particularly as optional arguments to a
- function, but also quite often as optional data members.
- The possible presence of a null pointer value makes the operations that
- access the pointee's value possibly undefined, therefore, expressions which
- use dereference and access operators, such as: `( *p = 2 )` and `( p->foo() )`,
- implicitly convey the notion of optionality, and this information is tied to
- the ['syntax] of the expressions. That is, the presence of operators `*` and `->`
- tell by themselves —without any additional context— that the expression will
- be undefined unless the implied pointee actually exist.
- Such a ['de facto] idiom for referring to optional objects can be formalized
- in the form of a concept: the __OPTIONAL_POINTEE__ concept.
- This concept captures the syntactic usage of operators `*`, `->` and
- contextual conversion to `bool` to convey the notion of optionality.
- However, pointers are good to [_refer] to optional objects, but not particularly
- good to handle the optional objects in all other respects, such as initializing
- or moving/copying them. The problem resides in the shallow-copy of pointer
- semantics: if you need to effectively move or copy the object, pointers alone
- are not enough. The problem is that copies of pointers do not imply copies of
- pointees. For example, as was discussed in the motivation, pointers alone
- cannot be used to return optional objects from a function because the object
- must move outside from the function and into the caller's context.
- A solution to the shallow-copy problem that is often used is to resort to
- dynamic allocation and use a smart pointer to automatically handle the details
- of this. For example, if a function is to optionally return an object `X`, it can
- use `shared_ptr<X>` as the return value. However, this requires dynamic allocation
- of `X`. If `X` is a built-in or small POD, this technique is very poor in terms of
- required resources. Optional objects are essentially values so it is very
- convenient to be able to use automatic storage and deep-copy semantics to
- manipulate optional values just as we do with ordinary values. Pointers do
- not have this semantics, so are inappropriate for the initialization and
- transport of optional values, yet are quite convenient for handling the access
- to the possible undefined value because of the idiomatic aid present in the
- __OPTIONAL_POINTEE__ concept incarnated by pointers.
- [heading Optional<T> as a model of OptionalPointee]
- For value access operations `optional<>` uses operators `*` and `->` to
- lexically warn about the possibly uninitialized state appealing to the
- familiar pointer semantics w.r.t. to null pointers.
- [caution
- However, it is particularly important to note that `optional<>` objects
- are not pointers. [_`optional<>` is not, and does not model, a pointer].
- ]
- For instance, `optional<>` does not have shallow-copy so does not alias:
- two different optionals never refer to the ['same] value unless `T` itself is
- a reference (but may have ['equivalent] values).
- The difference between an `optional<T>` and a pointer must be kept in mind,
- particularly because the semantics of relational operators are different:
- since `optional<T>` is a value-wrapper, relational operators are deep: they
- compare optional values; but relational operators for pointers are shallow:
- they do not compare pointee values.
- As a result, you might be able to replace `optional<T>` by `T*` on some
- situations but not always. Specifically, on generic code written for both,
- you cannot use relational operators directly, and must use the template
- functions __FUNCTION_EQUAL_POINTEES__ and __FUNCTION_LESS_POINTEES__ instead.
- [endsect]
- [endsect]
|