diff --git a/doc/optional.html b/doc/optional.html index 7ebdf9c..895312d 100644 --- a/doc/optional.html +++ b/doc/optional.html @@ -3,7 +3,10 @@ - + + + + Header @@ -42,7 +45,7 @@ HREF="../../../boost/optional/optional.hpp">boost/optional/optional.hpp>

There are different approaches to the issue of not having a value to return.

A typical approach is to consider the existence of a valid return value as a postcondition, so that if the function cannot compute the value to return, - it has either undefined behavior (and can use asssert in a debug build) + it has either undefined behavior (and can use assert in a debug build) or uses a runtime check and throws an exception if the postcondition is violated. This is a reasonable choice for example, for function (A), because the lack of a proper return value is directly related to an invalid parameter (out @@ -100,14 +103,15 @@ if ( p.second ) 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 inital value (c.f. 8.5.9).
+ indeterminate initial value (c.f. 8.5.9).
optional<T> intends to formalize the notion of initialization -(or loack of it) +(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 behaviour. That is, + 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 behaviour + 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 @@ -124,17 +128,18 @@ if ( p.second ) Using the Boost.Variant library, this model can be implemented in terms of boost::variant<T,nil_t>.
There is precedence for a discriminated union as a model for an optional value: the - Haskell Maybe builtin type constructor. + 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 neccesary in order to do so.

+ 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 writting I don't know of any precedence for a variable-size fixed-capacity (of 1) +

As of this writing I don't know of any precedence 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.

@@ -148,7 +153,8 @@ For instance, these models show the exact semantics required for a wrappe
  • 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 behaviour +
  • 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:

    @@ -158,7 +164,7 @@ of trying to access the value of an uninitialized optional
  • 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 behaviour +
  • Trying to extract a T from an empty container models the undefined behavior of trying to access the value of an uninitialized optional
  • @@ -171,7 +177,8 @@ 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 builtin type.

    +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:

    @@ -191,10 +198,10 @@ object.

    with a value obtained as a copy of some object.

    -

    Assignnment (upon initialized): To assign to the wrapped object the value +

    Assignment (upon initialized): To assign to the wrapped object the value of another wrapped object.

    -

    Assignnment (upon uninitialized): To initialize the 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 @@ -207,7 +214,7 @@ states.

    initialized or not.

    Swap: To exchange wrapped objects. (with whatever exception safety -guarantiees are provided by T's swap).

    +guarantees are provided by T's swap).

    De-initialization: To release the wrapped object (if any) and leave the wrapper in the uninitialized state.

    @@ -234,7 +241,7 @@ 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 choosen (which will be +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.

    @@ -259,7 +266,7 @@ optional objects: The operators * and -> 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 OptionalPointee concept.
    -This concept captures the syntactic usage of operatos *, -> and conversion to bool to convey +This concept captures the syntactic usage of operators *, -> and 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 @@ -273,22 +280,22 @@ them. The problem resides in the shallow-copy of pointer semantics: if you need 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 builtin or small POD, this technique is very poor in terms of required resources. + 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 unappropriate for the initialization and + 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 OptionalPointee concept incarnated by pointers.

    Optional<T> as a model of OptionalPointee

    For value access operations optional<> uses operators * and -> to lexically -warn about the possibliy uninitialized state appealing to the familiar pointer +warn about the possibly uninitialized state appealing to the familiar pointer semantics w.r.t. to null pointers.
    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<> has not shallow-copy so does not alias: two different optionals - never refer to the same value unless T itself is an reference (but my have equivalent values).
    +

    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; @@ -527,7 +534,7 @@ and its value is another reference to the same object referenced by *rhs; is uninitialized.

    Throws: Nothing.

    Notes: If rhs is initialized, both *this and *rhs will -refeer to the same object (they alias).

    +reefer to the same object (they alias).

    Example:

    optional<T&> uninit ;
    @@ -591,7 +598,7 @@ assert( *y == 123 ) ;
     

    Effect: Constructs an optional with a value of T obtained from the factory.

    Postconditions:  *this is initialized and its value is -directly given from the factory 'f' (i.e, the value is not copied).

    +directly given from the factory 'f' (i.e., the value is not copied).

    Throws: Whatever the T constructor called by the factory throws.

    Notes: See In-Place Factories

    Exception Safety: Exceptions can only be thrown during the call to the @@ -622,9 +629,9 @@ assert ( *y == v ) ;

    Effect: Assigns the value 'rhs' to an optional.

    Postconditions: *this is initialized and its value is a copy of rhs.

    -

    Throws: Whatever T::operator=( T const& ) or T::T(T conbst&) throws.

    +

    Throws: Whatever T::operator=( T const& ) or T::T(T const&) throws.

    Notes: If *this was initialized, T's assignment operator is -used, otherwise, its copy-contructor is used.

    +used, otherwise, its copy-constructor is used.

    Exception Safety: In the event of an exception, the initialization state of *this is unchanged and its value unspecified as far as optional is concerned (it is up to T's operator=()) [If *this is initially @@ -652,7 +659,7 @@ assert ( *opt == y ) ;

    Postconditions: *this is initialized and it references the same object referenced by rhs.

    Notes: If *this was initialized, is is rebound to the -new object. See here for details on this behaviour.

    +new object. See here for details on this behavior.

    Example:

    int a = 1 ;
    @@ -687,7 +694,7 @@ is uninitialized.
     

    Throws: Whatever T::operator( T const&) or  T::T( T const& ) throws.

    Notes: If both *this and rhs are initially initialized, T's assignment operator is used. If *this is initially initialized but -rhs is uinitialized, T's destructor is called. If *this is initially +rhs is uninitialized, T's destructor is called. If *this is initially uninitialized but rhs is initialized, T's copy constructor is called.

    Exception Safety: In the event of an exception, the initialization @@ -718,7 +725,8 @@ assert ( !def ) ; and it references the same object referenced by *rhs; otherwise, *this is uninitialized (and references no object).

    Notes: If *this was initialized and so is *rhs, this -is is rebound to the new object. See here for details on this behaviour.

    +is is rebound to the new object. See here for details on this +behavior.

    Example:

    int a = 1 ;
    @@ -755,7 +763,7 @@ to type T; else *this is uninitialized.
     

    Throws: Whatever T::operator=( U const& ) or T::T( U const& ) throws.

    Notes: If both *this and rhs are initially initialized, T's assignment operator (from U) is used. If *this is initially initialized but -rhs is uinitialized, T's destructor is called. If *this is initially +rhs is uninitialized, T's destructor is called. If *this is initially uninitialized but rhs is initialized, T's converting constructor (from U) is called.

    Exception Safety: In the event of an exception, the initialization @@ -1186,7 +1194,7 @@ else print("employer's name not found!"); };

    -

    Bypassing expensive unnecesary default construction

    +

    Bypassing expensive unnecessary default construction

    class ExpensiveCtor { ... } ;
     class Fred
     {
    @@ -1217,7 +1225,7 @@ value, a true real reference is stored so aliasing will ocurr: 

    • Copies of optional<T&> will copy the references but all these references - will nonetheless refeer to the same object.
    • + will nonetheless reefer to the same object.
    • Value-access will actually provide access to the referenced object rather than the reference itself.
    @@ -1262,13 +1270,13 @@ assert(b==3);

    Rationale:

    Rebinding semantics for the assignment of initialized optional -references has been choosen to provide consistency among initialization +references has been chosen to provide consistency among initialization states even at the expense of lack of consistency with the semantics of bare C++ references.
    It is true that optional<U> strives to behave as much as possible as U does whenever it is initialized; but in the case when U is T&, doing so would result -in incosistent behaviour w.r.t to the lvalue initialization state.

    -

    Imagine optional<T&> fordwarding assignment to the referenced object (thus +in inconsistent behavior w.r.t to the lvalue initialization state.

    +

    Imagine optional<T&> forwarding assignment to the referenced object (thus changing the referenced object value but not rebinding), and consider the following code :

      optional<int&> a = get();
    @@ -1280,11 +1288,11 @@ following code :

    What does the assignment do?
    If 'a' is uninitialized, the answer is clear: it binds to 'x' (we now have another reference to 'x').
    -But what if 'a' is already initiliazed? it would change the value of the +But what if 'a' is already initialized? it would change the value of the referenced object (whatever that is); which is inconsistent with the other possible case.

    If optional<T&> would assign just like T& does, you would never be able to -use Optional's assignment without explicitely handling the previous +use Optional's assignment without explicitly handling the previous initialization state unless your code is capable of functioning whether after the assignment, 'a' aliases the same object as 'b' or not.

    @@ -1330,7 +1338,7 @@ public:

    A solution to this problem is to support direct construction of the contained object right in the container's storage.
    -In this shceme, the user only needs to supply the arguments to the constructor +In this scheme, the user only needs to supply the arguments to the constructor to use in the wrapped object construction.

    class W
     {
    @@ -1350,12 +1358,12 @@ public:
     

    A limitation of this method is that it doesn't scale well to wrapped objects with multiple constructors nor to generic code were the constructor overloads are unknown.

    -

    The solution presented in this library is the familiy of InPlaceFactories and +

    The solution presented in this library is the family of InPlaceFactories and TypedInPlaceFactories.
    These factories are a family of classes which encapsulate an increasing number of arbitrary constructor parameters and supply a method to construct an object of a given type using those parameters at an address specified by the user via placement new.

    -

     For example, one member of this familiy looks like:

    +

     For example, one member of this family looks like:

    template<class T,class A0, class A1>
     class TypedInPlaceFactory2
     {