diff --git a/doc/optional.html b/doc/optional.html index 4c8f531..9c60bae 100644 --- a/doc/optional.html +++ b/doc/optional.html @@ -19,6 +19,8 @@ HREF="../../../boost/optional.hpp">boost/optional.hpp>
Synopsis
Semantics
Examples
+
Optional references
+
In-Place Factories
A note about optional<bool>
Exception Safety Guarantees
Type requirements
@@ -89,7 +91,7 @@ if ( p.second )

Development

-

The model

+

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. (c.f. 8.5)).
When a declaration includes a non-empty initializer (an initial value is given), it is said that @@ -104,91 +106,145 @@ if ( p.second ) 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 - 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. And given the formal treatment of initialization - states in optional objects, it is even possible to - reset an optional to uninitialized.

+ 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. - On modern languages, this can be modeled with a discriminated - union of T and something else such as a trivial POD or enum. - Discriminated unions are often called variants. - A variant has a current type, which in our case is either T or something else. In C++, - such a variant would be typically implemented as a template class of the form: variant<T,nil_t> -

-

There is precedence for a discriminated union as a model for an optional - value: the Haskell Maybe builtin type constructor.

-

A discriminated union, which can be seen as a container which has an object of either - type T or something else, has exactly the semantics required for a wrapper of optional values:

-
  • deep-copy semantics: copies of the variant implies copies of the contained value.
  • + the special value to the set of possible values of a given type. This super set of + T plus some nil_t—were 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 precedence for a discriminated union as a model for an optional value: the + Haskell Maybe builtin 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.

    +

    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) +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 behaviour of trying to access the value of an uninitialized optional
  • -

    Because of the way a discriminated union is used for this purpose, it only matters - whether its current type is T or not. We can put a layer on top of the variant hidding the other type - transforming a container of fixed size 1 into a variable size container which either has - a T or has nothing. Thus, the variant<T,nil_t> can be seen as if it were a variable-size - fixed-capacity stack-based container with the following optional-oriented interface:

    +
    +

    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 behaviour +of trying to access the value of an uninitialized optional
  • +
    +

    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 builtin type.

    -
    // Uninitialized (internally, current type is nil_t)
    -optional<T>::optional();
    +

    We can draw from the purpose of optional<T> the required basic semantics:

    -// Initialized with 'v' (internally, current type is T) -optional<T>::optional( T const& v ) ; +
    +

    Default Construction: To introduce a formally uninitialized wrapped +object.

    -// Back to uninitialized (current type is set to nil_t) -void optional<T>::reset(); +

    Direct Value Construction via copy: To introduce a formally +initialized wrapped object whose value is obtained as a copy of some object.

    -// Assigns 'v' whether previously initialized or not (current type is set to T) -void optional<T>::reset( T const& v ) ; +

    Deep Copy Construction: To obtain a different yet equivalent wrapped +object.

    -// Returns 'true' if the optional is initialized, 'false' otherwise. -bool optional<T>::initialized() ; +

    Direct Value Assignment (upon initialized): To assign the wrapped object a value obtained +as a copy of some object.

    -// If the optional is initialized (current type is T), returns a reference to its value. -// Otherwise (current type is nil_t), the result is undefined. -T const& optional<T>::ref() const ; -T& optional<T>::ref() ; +

    Direct Value Assignment (upon uninitialized): To initialize the wrapped object +with a value obtained +as a copy of some object.

    -// If both are initialized, calls swap(T&,T&); -// If only one is initialized, calls reset(T const&) and reset(). -// If both are uninitalized, do nothing. -void swap ( optional<T>& lhs, optional<T>& rhs ) ; +

    Assignnment (upon initialized): To assign the wrapped object a value obtained as a copy +of another wrapper's object.

    -// If both are initialized, compare values. -// If only one is initialized, they are not equal. -// If both are uninitalized, they are equal. -bool operator == ( optional<T> const& lhs, optional<T> const& rhs ) ; -bool operator != ( optional<T> const& lhs, optional<T> const& rhs ) ; -
    +

    Assignnment (upon uninitialized): To initialize the wrapped object +with value obtained as a copy +of another wrapper's object.

    -

    Pointers and optional objects

    -

    In C++, unlike many other languages, objects can be referenced indirectly - by means of a pointer (or a reference). Pointers have several nice features, - two of which are relevant to this development.

    -

    One is that a pointer has its own pointer value, which in effect - references the object being pointed to: the pointee. Consequently, - copies of pointers do not involve copies of pointees. This effect results in aliasing: - different pointers can refer to the same object. - The particular semantic that a copy of a pointer does not involve - a copy of the pointee is called shallow-copy, which is oppossed to deep-copy were - a copy of a wrapper involves a copy of the wrapped object (as with optional<>)
    - Since this is the semantic followed by pointers (and references), shallow-copy - (and therefore aliasing) is implied in pointer semantics.

    -

    The other relevant feature of a pointer is that a pointer can have a null +

    Deep Relational Operations (when supported by the type T): To compare +wrapped object values taking into account the presence of uninitialized +operands.

    + +

    Value access: To unwrap the wrapped object.

    + +

    Initialization state query: To determine if the object is formally +initialized or not.

    + +

    Swap: To exchange wrapper's objects. (with whatever exception safety +guarantiees 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.

    +

    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 choosen (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.

    +

    Lexically-hinted Value Access in the presence of possibly untitialized +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 inexistent objects.

    -

    This meaning of the null pointer value allowed pointers to became a defacto standard +

    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 @@ -200,14 +256,11 @@ bool operator != ( optional<T> const& lhs, optional<T> const& 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.
    - Furthermore, the existence of the pointee can be tested by a comparison against - the null pointer value or via a conversion to bool, which allows expressions of - the form: if ( p != 0 ), or if ( p ) to be used to test for the existence of the pointee.

    -

    Such a defacto idiom for referring to optional objects can be formalized in the form of a + 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 -the notion of optionality.

    +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 @@ -226,29 +279,25 @@ them. The problem resides in the shallow-copy of pointer semantics: if you need values. Pointers do not have this semantics, so are unappropriate 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.
    - Therefore, the final solution which is presented in this library is to shape the - previously discussed optional—which is a value-based container—as a model - of the OptionalPointee concept. + concept incarnated by pointers.

    -

    Optional<T> as a model of OptionalPointee

    -

    The optional<> template class presented with this library is a variation of the - sketch shown before (as a layer on top of a variant). It features deep-copy and - deep relational operators, but also models the OptionalPointee concept. - Instead of the member function 'initialized()' it has a safe conversion to bool, - and instead of the 'ref()' member function, it has operators*() and ->().
    - However, it is particularly important that optional<> objects are not mistaken by pointers, - they are not. optional<> 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 (but my have equivalent values).
    +

    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 +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).
    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 function - equal_pointees() instead. + operators directly, and must use the template functions + equal_pointees() and + less_pointees() instead.


    Synopsis

    @@ -260,20 +309,36 @@ class optional { public : + (If T is of referennce type, the parameters and results by reference are by value) + optional () ; - explicit optional ( T const& v ) ; + optional ( detail::none_t ) ; - optional ( optional const& rhs ) ; + optional ( T const& v ) ; + + optional ( optional const& rhs ) ; template<class U> explicit optional ( optional<U> const& rhs ) ; + + template<class InPlaceFactory> explicit optional ( InPlaceFactory const& f ) ; + + template<class TypedInPlaceFactory> explicit optional ( TypedInPlaceFactory const& f ) ; + + optional& operator = ( detail::none_t ) ; + + optional& operator = ( T const& v ) ; optional& operator = ( optional const& rhs ) ; template<class U> optional& operator = ( optional<U> const& rhs ) ; - T const* get() const ; - T* get() ; + template<class InPlaceFactory> optional& operator = ( InPlaceFactory const& f ) ; + + template<class TypedInPlaceFactory> optional& operator = ( TypedInPlaceFactory const& f ) ; + + T const& get() const ; + T& get() ; T const* operator ->() const ; T* operator ->() ; @@ -281,13 +346,18 @@ class optional T const& operator *() const ; T& operator *() ; - void reset(); - - void reset ( T const& ) ; + T const* get_ptr() const ; + T* get_ptr() ; operator unspecified-bool-type() const ; bool operator!() const ; + + deprectated methods + + void reset() ; (deprectated) + void reset ( T const& ) ; (deprectated) + bool is_initialized() const ; (deprectated) } ; @@ -295,7 +365,25 @@ template<class T> inline bool operator == ( optional<T> const& x, op template<class T> inline bool operator != ( optional<T> const& x, optional<T> const& y ) ; -template<class T> inline T* get_pointer ( optional<T> const& opt ) ; +template<class T> inline bool operator < ( optional<T> const& x, optional<T> const& y ) ; + +template<class T> inline bool operator > ( optional<T> const& x, optional<T> const& y ) ; + +template<class T> inline bool operator <= ( optional<T> const& x, optional<T> const& y ) ; + +template<class T> inline bool operator >= ( optional<T> const& x, optional<T> const& y ) ; + +template<class T> inline T const& get ( optional<T> const& opt ) ; + +template<class T> inline T& get ( optional<T> & opt ) ; + +template<class T> inline T const* get ( optional<T> const* opt ) ; + +template<class T> inline T* get ( optional<T>* opt ) ; + +template<class T> inline T const* get_pointer ( optional<T> const& opt ) ; + +template<class T> inline T* get_pointer ( optional<T> & opt ) ; template<class T> inline void swap( optional<T>& x, optional<T>& y ) ; @@ -304,12 +392,24 @@ template<class T> inline void swap( optional<T>& x, optional<T>
    -

    Semantics

    +

    Detailed Semantics

    + +

    NOTES:

    + +

    Because T might be of reference type, in the sequel, those entries whose +semantic depends on T being of reference type or not will be distinguished using +the following convention:
    +If the entry reads: optional<T (not a ref)>, the description corresponds only to +the case where T is not of reference type.
    +If the entry reads: optional<T&>, the description corresponds only to the case +where T is of reference type.
    +If the entry reads: optional<T>, the description is the same for both cases.

    + +

    The following section contains various assert() which are used only to +show the postconditions as sample code. It is not implied that the type T must +support each particular expression but that if the expression is supported, the +implied condition holds.

    -

    Note: the following section contains various assert() which are used only to - show the postconditions as sample code. - It is not implied that the type T must support each particular expression - but that if the expression is supported, the implied condition holds.


    optional<T>::optional();
    @@ -327,35 +427,74 @@ assert ( !def ) ;
    -
    explicit optional<T>::optional( T const& v )
    +
    optional<T>::optional( detail::none_t );
    -

    Effect: Directly-Constructs an optional.

    -

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

    -

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

    -

    Notes: T::T( T const& ) is called.

    -

    Exception Safety: Exceptions can only be thrown during T::T( T const& ); -in that case, this constructor has no effect. -

    +

    Effect: Constructs an optional uninitialized.

    +

    Postconditions: *this is uninitialized.

    +

    Throws: Nothing.

    +

    Notes:

    +
    +

    T's default constructor is not called.
    +The +expression boost::none denotes an instance of boost::detail::none_t that can be +used as the parameter.

    +

    Example:

    -
    -
    -T v;
    -optional<T> opt(v);
    -assert ( *opt == v ) ;
    -
    +
    +
    optional<T> n(none) ;
    +assert ( !n ) ;

    -
    optional<T>::optional( optional const& rhs );
    +
    optional<T (not a ref)>::optional( T const& v )
    +
    +

    Effect: Directly-Constructs an optional.

    + +

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

    +

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

    +

    Notes: T::T( T const& ) is called.

    +

    Exception Safety: Exceptions can only be thrown during T::T( T const& ); +in that case, this constructor has no effect. +

    +

    Example:

    +
    +
    T v;
    +optional<T> opt(v);
    +assert ( *opt == v ) ;
    +
    +
    + +
    + +
    optional<T&>::optional( T ref )
    +
    +

    Effect: Directly-Constructs an optional.

    +

    Postconditions: *this is initialized and its value is an +instance of an internal type wrapping the reference 'ref'.

    +

    Throws: Nothing.

    +

    Example:

    +
    +
    T v;
    +T& vref = v ;
    +optional<T&> opt(vref);
    +assert ( *opt == v ) ;
    +++ v ; // mutate referee
    +assert (*opt == v); 
    +
    +
    + +
    + +
    optional<T (not a ref)>::optional( optional const& rhs );

    Effect: Copy-Constructs an optional.

    Postconditions: If rhs is initialized, *this is initialized and its value is a copy of the value of rhs; else *this is uninitialized.

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

    -

    Notes: T::T( T const& ) is called if rhs is initialized.

    +

    Notes: If rhs is initialized, T::T(T const& ) is called.

    Exception Safety: Exceptions can only be thrown during T::T( T const& ); in that case, this constructor has no effect.

    @@ -379,7 +518,38 @@ assert ( init2 == init ) ;
    -
    explicit template<U> optional<T>::optional( optional<U> const& rhs );
    +
    optional<T&>::optional( optional const& rhs );
    +
    +

    Effect: Copy-Constructs an optional.

    +

    Postconditions: If rhs is initialized, *this is initialized +and its value is a copy of the internal wrapper holding the references in rhs; else *this +is uninitialized.

    +

    Throws: Nothing.

    +

    Notes: If rhs is initialized, the internal wrapper will be +copied and just like true references, both *this and rhs will +referr to the same object (will alias).

    +

    Example:

    +
    +
    optional<T&> uninit ;
    +assert (!uninit);
    +
    +optional<T&> uinit2 ( uninit ) ;
    +assert ( uninit2 == uninit );
    +
    +T v = 2 ; T& ref = v ;
    +optional<T> init(ref);
    +assert ( *init == v ) ;
    +
    +optional<T> init2 ( init ) ;
    +assert ( *init2 == v ) ;
    +
    + +
    +
    + +
    + +
    template<U> explicit optional<T (not a ref)>::optional( optional<U> const& rhs );

    Effect: Copy-Constructs an optional.

    Postconditions: If rhs is initialized, *this is initialized @@ -407,7 +577,67 @@ assert( *y == 123 ) ;


    -
    optional& optional<T>::operator= ( optional const& rhs ) ;
    +
    template<InPlaceFactory> explicit optional<T (not a ref)>::optional( InPlaceFactory const& f );
    + +
    template<TypedInPlaceFactory> explicit optional<T (not a ref)>::optional( TypedInPlaceFactory const& f );
    +
    +

    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).

    +

    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 +T constructor used by the factory; +in that case, this constructor has no effect. +

    +

    Example:

    +
    + +
    class C { C ( char, double, std::string ) ; } ;
    +
    +C v('A',123.4,"hello");
    +
    +optional<C> x( in_place   ('A', 123.4, "hello") ); // InPlaceFactory used
    +optional<C> y( in_place<C>('A', 123.4, "hello") ); // TypedInPlaceFactory used
    +
    +assert ( *x == v ) ;
    +assert ( *y == v ) ;
    +
    +
    +
    +
    + +
    + +
    optional& optional<T (not a ref)>::operator= ( T const& rhs ) ;
    +
    +

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

    +

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

    +

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

    +

    Notes: If *this was initialized, it is first reset to uninitialized +using T::~T(), then T::T(rhs) is called.

    +

    Exception Safety: Basic: Exceptions can only be thrown during T::T( T const& ); +in that case, *this is left uninitialized. +

    +

    Example:

    +
    +
    T x;
    +optional<T> opt(x) ;
    +
    +T y;
    +opt = y ;
    +assert ( *opt == y ) ;
    +// previous value (copy of 'v') destroyed from within 'opt'.
    +
    +
    +
    +
    + +
    + +
    optional& optional<T (not a ref)>::operator= ( optional const& rhs ) ;

    Effect: Assigns another optional to an optional.

    Postconditions: If rhs is initialized, *this is initialized @@ -423,8 +653,7 @@ in that case, *this is left uninitialized.

    Example:

    -
    -T v;
    +    
    T v;
     optional<T> opt(v);
     optional<T> uninit ;
     
    @@ -438,7 +667,7 @@ assert ( !opt ) ;
     
     
    -
    template<U> optional& optional<T>::operator= ( optional<U> const& rhs ) ;
    +
    template<U> optional& optional<T (not a ref)>::operator= ( optional<U> const& rhs ) ;

    Effect: Assigns another convertible optional to an optional.

    Postconditions: If rhs is initialized, *this is initialized @@ -455,8 +684,7 @@ in that case, *this is left uninitialized.

    Example:

    -
    -T v;
    +    
    T v;
     optional<T> opt0(v);
     optional<U> opt1;
     
    @@ -467,108 +695,30 @@ assert ( *opt1 == static_cast<U>(v) ) ;
     

    -
    void optional<T>::reset( T const& v ) ;
    +
    void optional<T (not a ref)>::reset( T const& v ) ;
    -

    Effect: Resets the current value.

    -

    Postconditions: *this is initialized and its value is -a copy of 'v'. -

    -

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

    -

    Notes: If *this was initialized, it is first reset to uninitialized -using T::~T(), then T::T( T const& ) is called. -

    -

    Exception Safety: Basic: Exceptions can only be thrown during T::T( T const& ); -in that case, *this is left uninitialized. -

    -

    Example:

    -
    -
    optional<T> opt ( some_T ) ;
    -assert( *opt == some_T );
    -opt.reset ( some_other_T ) ;
    -assert( *opt == some_other_T );
    -
    -
    +

    Deprecated: same as operator= ( T const& v) ;


    void optional<T>::reset() ;
    -

    Effect: Destroys the current value.

    -

    Postconditions: *this is uninitialized.

    -

    Throws: Nothing.

    -

    Notes: T::~T() is called.

    -

    Example:

    -
    -
    optional<T> opt ( some_T ) ;
    -assert( *opt == some_T );
    -opt.reset();
    -assert( !opt );
    -
    -
    +

    Deprecated: Same as operator=( detail::none_t );


    -
    -T const* optional<T>::get() const ;
    -T*       optional<T>::get() ;
     
    -inline T const* get_pointer ( optional<T> const& ) ;
    -inline T*       get_pointer ( optional<T>&) ;
    +
    T const& optional<T (not a ref)>::operator*() const ;
    +T&       optional<T (not a ref)>::operator*();
    + +
    T const& optional<T (not a ref)>::get() const ;
    +T&       optional<T (not a ref)>::get() ;
    +
    +inline T const& get ( optional<T (not a ref)> const& ) ;
    +inline T&       get ( optional<T (not a ref)> &) ;
     
    -

    Returns: If *this is initialized, a pointer to the contained -value; else 0 (null). -

    -

    Throws: Nothing.

    -

    Notes: The contained value is permanently stored within *this, so -you should not hold nor delete this pointer -

    -

    Example:

    -
    -
    -T v;
    -optional<T> opt(v);
    -optional<T> const copt(v);
    -T* p = opt.get() ;
    -T const* cp = copt.get();
    -assert ( p == get_pointer(opt) );
    -assert ( cp == get_pointer(copt) ) ;
    -
    -
    -
    - - -
    - - -
    -T const* optional<T>::operator ->() const ;
    -T*       optional<T>::operator ->()       ;
    -
    -
    -

    Requirements: *this is initialized.

    -

    Returns: A pointer to the contained value.

    -

    Throws: Nothing.

    -

    Notes: The requirement is asserted via BOOST_ASSERT().

    -

    Example:

    -
    -
    -struct X { int mdata ; } ;
    -X x ;
    -optional<X> opt (x);
    -opt->mdata = 2 ;
    -
    -
    -
    - - -
    - - -
    T const& optional<T>::operator*() const ;
    -T&       optional<T>::operator*();
    -

    Requirements: *this is initialized

    Returns: A reference to the contained value

    Throws: Nothing.

    @@ -584,9 +734,93 @@ T w ; assert ( *opt == w ) ;
    +
    
     

    + + +
    T const& optional<T&>::operator*() const ;
    +T      & optional<T&>::operator*();
    + +
    T const& optional<T&>::get() const ;
    +T&       optional<T&>::get() ;
    +
    +inline T const& get ( optional<T&> const& ) ;
    +inline T&       get ( optional<T&> &) ;
    +
    +
    +

    Requirements: *this is initialized

    +

    Returns: The reference contained.

    +

    Throws: Nothing.

    +

    Notes: The requirement is asserted via BOOST_ASSERT().

    +

    Example:

    +
    +
    T v ;
    +T& vref = v ;
    +optional<T&> opt ( vref );
    +T const& vref2 = *opt;
    +assert ( vref2 == v ) ;
    +++ v ;
    +assert ( *opt == v ) ;
    +
    +
    + +
    + +
    T const* optional<T (not a ref)>::get_ptr() const ;
    +T*       optional<T (not a ref)>::get_ptr() ;
    +
    +inline T const* get_pointer ( optional<T (not a ref)> const& ) ;
    +inline T*       get_pointer ( optional<T (not a ref)> &) ;
    +
    +
    +

    Returns: If *this is initialized, a pointer to the contained +value; else 0 (null). +

    +

    Throws: Nothing.

    +

    Notes: The contained value is permanently stored within *this, so +you should not hold nor delete this pointer +

    +

    Example:

    +
    +
    T v;
    +optional<T> opt(v);
    +optional<T> const copt(v);
    +T* p = opt.get_ptr() ;
    +T const* cp = copt.get_ptr();
    +assert ( p == get_pointer(opt) );
    +assert ( cp == get_pointer(copt) ) ;
    +
    +
    +
    + + +
    + + +
    T const* optional<T (not a ref)>::operator ->() const ;
    +T*       optional<T (not a ref)>::operator ->()       ;
    +
    +
    +

    Requirements: *this is initialized.

    +

    Returns: A pointer to the contained value.

    +

    Throws: Nothing.

    +

    Notes: The requirement is asserted via BOOST_ASSERT().

    +

    Example:

    +
    +
    struct X { int mdata ; } ;
    +X x ;
    +optional<X> opt (x);
    +opt->mdata = 2 ;
    +
    +
    +
    + + +
    + +
    optional<T>::operator unspecified-bool-type() const ;

    Returns: An unspecified value which if used on a boolean context is equivalent to (get() != 0)

    @@ -623,6 +857,23 @@ assert ( !!opt ) ;
    + +
    + + +
    bool optional<T>::is_initialized() const ;
    +
    +

    Returns: true is the optional is initialized, false +otherwise.

    +

    Throws: Nothing.

    +
    +
    optional<T> def ;
    +assert ( !def.is_initialized() );
    +optional<T> opt ( v ) ;
    +assert ( opt.is_initialized() );
    +
    +
    +
    @@ -639,8 +890,7 @@ use equal_pointees() inst

    Example:

    -
    -T x(12);
    +    
    T x(12);
     T y(12);
     T z(21);
     optional<T> def0 ;
    @@ -666,6 +916,46 @@ assert ( optX != optZ ) ;
     
    +
    + + +
    bool operator < ( optional<T> const& x, optional<T> const& y );
    +
    +

    Returns: If both x and y are initialied, (*x < *y). +If only x or y is initialized, false. If both are uninitialized, +false. +

    +

    Throws: Nothing.

    +

    Notes: Pointers have shallow relational operators while optional has +deep relational operators. Do not use operator < directly in generic code +which expect to be given either an optional<T> or a pointer; +use less_pointees() instead +

    +

    Example:

    +
    +
    T x(12);
    +T y(34);
    +optional<T> def ;
    +optional<T> optX(x);
    +optional<T> optY(y);
    +
    +// Identity always hold
    +assert ( !(def < def) );
    +assert ( optX == optX );
    +
    +// Both uninitialized compare equal
    +assert ( def0 == def1 );
    +
    +// Only one initialized compare unequal.
    +assert ( def0 != optX );
    +
    +// Both initialized compare as (*lhs == *rhs)
    +assert ( optX == optY ) ;
    +assert ( optX != optZ ) ;
    +
    +
    +
    +
    bool operator != ( optional<T> const& x, optional<T> const& y );
     
    @@ -674,6 +964,30 @@ assert ( optX != optZ ) ;

    Throws: Nothing.

    +
    +
    bool operator > ( optional<T> const& x, optional<T> const& y );
    +
    +
    +

    Returns: !( y < x );

    +

    Throws: Nothing.

    +
    + +
    +
    bool operator <= ( optional<T> const& x, optional<T> const& y );
    +
    +
    +

    Returns: !( y<x );

    +

    Throws: Nothing.

    +
    + +
    +
    bool operator >= ( optional<T> const& x, optional<T> const& y );
    +
    +
    +

    Returns: !( x<y );

    +

    Throws: Nothing.

    +
    +
    void swap ( optional<T>& x, optional<T>& y );
    @@ -698,8 +1012,7 @@ If only one is initialized, it has the same basic guarantee as optional&l

    Example:

    -
    -T x(12);
    +      
    T x(12);
     T y(21);
     optional<T> def0 ;
     optional<T> def1 ;
    @@ -728,17 +1041,17 @@ assert ( *optY == x );
     

    Optional return values

    optional<char> get_async_input()
     {
    -  if ( !queue.empty() )
    -       return optional<char>(queue.top());
    -  else return optional<char>(); // uninitialized
    +  if ( !queue.empty() )
    +       return optional<char>(queue.top());
    +  else return optional<char>(); // uninitialized
     }
     
    -void recieve_async_message()
    +void receive_async_message()
     {
    -  optional<char> rcv ;
    -  // The safe boolean conversion from 'rcv' is used here.
    -  while ( (rcv = get_async_input()) && !timeout() )
    -    output(*rcv);
    +  optional<char> rcv ;
    +  // The safe boolean conversion from 'rcv' is used here.
    +  while ( (rcv = get_async_input()) && !timeout() )
    +    output(*rcv);
     }
     
    @@ -746,49 +1059,49 @@ void recieve_async_message()
    optional<string> name ;
     if ( database.open() )
     {
    -  name.reset ( database.lookup(employer_name) ) ;
    +  name.reset ( database.lookup(employer_name) ) ;
     }
     else
     {
    -  if ( can_ask_user )
    -    name.reset ( user.ask(employer_name) ) ;
    +  if ( can_ask_user )
    +    name.reset ( user.ask(employer_name) ) ;
     }
     
     if ( name )
    -     print(*name);
    +     print(*name);
     else print("employer's name not found!");
     

    Optional data members

    class figure
     {
    -  public:
    +  public:
     
    -    figure()
    -    {
    -      // data member 'm_clipping_rect' is uninitialized at this point.
    -    }
    +    figure()
    +    {
    +      // data member 'm_clipping_rect' is uninitialized at this point.
    +    }
     
    -    void clip_in_rect ( rect const& rect )
    -      {
    -         ....
    -         m_clipping_rect.reset ( rect ) ; // initialized here.
    -      }
    +    void clip_in_rect ( rect const& rect )
    +      {
    +         ....
    +         m_clipping_rect.reset ( rect ) ; // initialized here.
    +      }
     
    -    void draw ( canvas& cvs )
    -      {
    -        if ( m_clipping_rect )
    -          do_clipping(*m_clipping_rect);
    +    void draw ( canvas& cvs )
    +      {
    +        if ( m_clipping_rect )
    +          do_clipping(*m_clipping_rect);
     
    -        cvs.drawXXX(..);
    -      }
    +        cvs.drawXXX(..);
    +      }
     
    -    // this can return NULL.
    -    rect const* get_clipping_rect() { return get_pointer(m_clipping_rect); }
    +    // this can return NULL.
    +    rect const* get_clipping_rect() { return get_pointer(m_clipping_rect); }
     
    -  private :
    +  private :
     
    -    optional<rect> m_clipping_rect ;
    +    optional<rect> m_clipping_rect ;
     
     };
     
    @@ -796,51 +1109,212 @@ else print("employer's name not found!");
    class ExpensiveCtor { ... } ;
     class Fred
     {
    -  Fred() : mLargeVector(10000) {}
    +  Fred() : mLargeVector(10000) {}
     
    -  std::vector< optional<ExpensiveCtor> > mLargeVector ;
    +  std::vector< optional<ExpensiveCtor> > mLargeVector ;
     } ;
     

    +

    Optional references

    +

    This library allow the template parameter T to be of reference type: T&, and +to some extent, T const&.

    + +

    However, since references are not real objects some restrictions apply and +some operations are not available in this case:

    + +
      +
    • Converting constructors
    • +
    • Converting assignment
    • +
    • InPlace construction
    • +
    • InPlace assignment
    • +
    • Value-access via pointer
    • +
    +

    Also, even though optional<T&> treats it wrapped pseudo-object much as a real +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.
    • +
    • Value-access will actually provide access to the referenced object rather + than the reference itself.
    • +
    + +
    + +

    In-Place Factories

    +

    +One of the typical problems with wrappers and containers is that their +interfaces usually provide an operation to initialize or assign the contained +object as a copy of some other object. This not only requires the underlying +type to be Copy Constructible, but also requires the existence of a fully +constructed object, often temporary, just to follow the copy from:

    +
    struct X 
    +{
    +  X ( int, std:::string ) ; 
    +} ;
    +
    class W
    +{
    +  X wrapped_ ;
    +  
    +public:
    +
    +  W ( X const& x ) : wrapped_(x) {}  
    +} ;
    +
    void foo()
    +{
    +  // Temporary object created.
    +  W ( X(123,"hello") ) ; 
    +}
    +
    +

    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 +to use in the wrapped object construction.

    +
    class W
    +{
    +  X wrapped_ ;
    +  
    +public:
    +
    +  W ( X const& x ) : wrapped_(x) {}  
    +  W ( int a0, std::string a1) : wrapped_(a0,a1) {}
    +} ;
    +
    void foo()
    +{
    +  // Wrapped object constructed in-place
    +  // No temporary created.
    +  W (123,"hello") ; 
    +}
    +
    +

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

    +
    template<class T,class A0, class A1>
    +class TypedInPlaceFactory2
    +{
    +  A0 m_a0 ; A1 m_a1 ;
    +  
    +public:
    +
    +  TypedInPlaceFactory2( A0 const& a0, A1 const& a1 ) : m_a0(a0), m_a1(a1) {}
    +
    +  void construct ( void* p ) { new (p) T(m_a0,m_a1) ; }  
    +} ;
    +
    +

    A wrapper class aware of this can use it as:

    +
    class W
    +{
    +  X wrapped_ ;
    +  
    +public:
    +
    +  W ( X const& x ) : wrapped_(x) {}  
    +  W ( TypedInPlaceFactory2 const& fac ) { fac.construct(&wrapped_) ; }
    +} ;
    +
    void foo()
    +{
    +  // Wrapped object constructed in-place via a TypedInPlaceFactory.
    +  // No temporary created.
    +  W ( TypedInPlaceFactory2<X,int,std::string&rt;(123,"hello")) ; 
    +}
    +
    +

    The factories are divided in two groups:

      +
    • TypedInPlaceFactories: those which take the target type as a primary template parameter.
    • +
    • InPlaceFactories: those with a template construct(void*) member function taking the target type.
    • +
    +

    Within each group, all the family members differ only in the number of parameters allowed.

    +

    +

    This library provides an overloaded set of helper template functions to construct these factories +without requiring unnecessary template parameters:

    +
    template<class A0,...,class AN> 
    +InPlaceFactoryN <A0,...,AN> in_place ( A0 const& a0, ..., AN const& aN) ;
    +
    +template<class T,class A0,...,class AN> 
    +TypedInPlaceFactoryN <T,A0,...,AN> in_place ( T const& a0, A0 const& a0, ..., AN const& aN) ;
    + +

    In-place factories can be used generically by the wrapper and user as follows:

    +
    class W
    +{
    +  X wrapped_ ;
    +  
    +public:
    +
    +  W ( X const& x ) : wrapped_(x) {}  
    +  
    +  template
    +  W ( InPlaceFactory const& fac ) { fac.template <X>construct(&wrapped_) ; }
    +  
    +} ;
    +
    void foo()
    +{
    +  // Wrapped object constructed in-place via a InPlaceFactory.
    +  // No temporary created.
    +  W ( in_place(123,"hello") ) ; 
    +}
    +
    +

    The factories are implemented in the headers: +in_place_factory.hpp and +typed_in_place_factory.hpp +

    + +
    +

    A note about optional<bool>

    optional<bool> should be used with special caution and consideration.

    -

    First, it is functionally similar to a tristate boolean (false,maybe,true) —such as -boost::tribool (not yet formally in boost)—except that in a tristate boolean, +

    First, it is functionally similar to a tristate boolean (false,maybe,true) —such as boost::tribool (not yet formally in boost)—except that in a tristate boolean, the maybe state represents a valid value, unlike the corresponding state of an uninitialized optional<bool>.
    It should be carefully considered if an optional bool instead of a tribool is really needed

    Second, optional<> provides an implicit conversion to bool. This conversion refers to the initialization state and not to the contained value.
    Using optional<bool> can lead to subtle errors due to the implicit bool conversion:

    -
    -void foo ( bool v ) ;
    +
    void foo ( bool v ) ;
     void bar()
     {
    -  optional<bool> v = try();
    +  optional<bool> v = try();
     
    -  // The following intended to pass the value of 'v' to foo():
    -  foo(v);
    -  // But instead, the initialization state is passed
    -  // due to a typo: it should have been foo(*v).
    +  // The following intended to pass the value of 'v' to foo():
    +  foo(v);
    +  // But instead, the initialization state is passed
    +  // due to a typo: it should have been foo(*v).
     }
     

    The only implicit conversion is to bool, and it is safe in the sense that typical -integral promotions don't apply (i.e. if foo() takes an 'int' instead, it won't compile). -


    +integral promotions don't apply (i.e. if foo() takes an 'int' instead, it won't compile).

    Exception Safety Guarantees

    Assignment and Reset:

    -

    Because of the current implementation (see Implementation Notes), - optional<T>::operator=( optional<T> const& ) -and optional<T>::reset( T const& ) -can only guarantee the basic exception safety: The lvalue optional is left -uninitialized if an exception is thrown (any previous value is first -destroyed using T::~T())

    -

    optional<T>::reset() provides the no-throw guarantee (assuming a no-throw T::~T())

    +

    Because of the current implementation (see Implementation Notes), all +of the assignment methods:

    +
      +
    • optional<T>::operator= ( optional<T> const& ) +
    • +
    • optional<T>::operator= ( T const& )
    • +
    • template<class U> optional<T>::operator= ( optional<U> const& ) +
    • +
    • template<class InPlaceFactory> optional<T>::operator= ( + InPlaceFactory const& )
    • +
    • template<class TypedInPlaceFactory> optional<T>::operator= ( + TypedInPlaceFactory const& )
    • +
    • optional<T>:::reset ( T const&)
    • +
    +

    Can only guarantee the basic exception safety: The lvalue optional is left uninitialized +if an exception is thrown (any previous value is first destroyed using T::~T())

    +

    On the other hand, the uninitializing methods:

    +
      +
    • optional<T>::operator= ( detail::none_t )
    • +
    • optional<T>::reset()
    • +
    +

    Provide the no-throw guarantee (assuming a no-throw T::~T())

    However, since optional<> itself doesn't throw any exceptions, -the only source for exceptions here is T's copy constructor, so if you know the exception guarantees +the only source for exceptions here are T's constructor, so if you know the exception guarantees for T::T ( T const& ), you know that optional's assignment and reset has the same guarantees.

    //
     // Case 1: Exception thrown during assignment.
    @@ -849,17 +1323,17 @@ T v0(123);
     optional<T> opt0(v0);
     try
     {
    -  T v1(456);
    -  optional<T> opt1(v1);
    -  opt0 = opt1 ;
    +  T v1(456);
    +  optional<T> opt1(v1);
    +  opt0 = opt1 ;
     
    -  // If no exception was thrown, assignment succeeded.
    -  assert( *opt0 == v1 ) ;
    +  // If no exception was thrown, assignment succeeded.
    +  assert( *opt0 == v1 ) ;
     }
     catch(...)
     {
    -  // If any exception was thrown, 'opt0' is reset to uninitialized.
    -  assert( !opt0 ) ;
    +  // If any exception was thrown, 'opt0' is reset to uninitialized.
    +  assert( !opt0 ) ;
     }
     
     //
    @@ -869,58 +1343,48 @@ T v0(123);
     optional<T> opt(v0);
     try
     {
    -  T v1(456);
    -  opt.reset ( v1 ) ;
    +  T v1(456);
    +  opt.reset ( v1 ) ;
     
    -  // If no exception was thrown, reset succeeded.
    -  assert( *opt == v1 ) ;
    +  // If no exception was thrown, reset succeeded.
    +  assert( *opt == v1 ) ;
     }
     catch(...)
     {
    -  // If any exception was thrown, 'opt' is reset to uninitialized.
    -  assert( !opt ) ;
    +  // If any exception was thrown, 'opt' is reset to uninitialized.
    +  assert( !opt ) ;
     }
     

    Swap:

    -

    void swap( optional<T>&, optional<T>& ) -has the same exception guarantee as swap(T&,T&) when both optionals are initialized.
    -If only one of the optionals is initialized, it gives the same -basic exception guarantee as optional<T>::reset( T const& ) -(since optional<T>::reset() doesn't throw).
    -If none of the optionals is initialized, it has no-throw guarantee since it is a no-op. -

    +

    void swap( optional<T>&, optional<T>& ) has the same exception guarantee as swap(T&,T&) when both optionals are initialized.
    +If only one of the optionals is initialized, it gives the same basic exception guarantee as optional<T>::reset( T const& ) (since optional<T>::reset() doesn't throw).
    +If none of the optionals is initialized, it has no-throw guarantee since it is a no-op.


    Type requirements

    -

    T must be Copy Constructible -and have a no-throw destructor.
    -T is not required to be -Default Constructible -

    +

    In general, T must be Copy Constructible and have a no-throw destructor. The copy-constructible requirement is not needed +if InPlaceFactories are used.
    +T is not required to be Default Constructible


    Implementation Notes

    optional<T> is currently implemented - using a custom aligned storage facility built from alignment_of and - type_with_alignment (both from Type Traits). + using a custom aligned storage facility built from alignment_of and type_with_alignment (both from Type Traits). It uses a separate boolean flag to indicate the initialization state.
    Placement new with T's copy constructor and T's destructor are explicitly used to initialize,copy and destroy optional values.
    As a result, T's default constructor is effectively by-passed, but the exception guarantees are basic.
    It is planned to replace the current implementation with another with - stronger exception safety, such as a future boost::variant. -

    + stronger exception safety, such as a future boost::variant.


    Dependencies and Portability

    -

    The implementation uses type_traits/alignment_of.hpp -and type_traits/type_with_alignment.hpp

    -

    It has been tested on bcc5.5.1, vc6.0 and gcc2.95.2

    +

    The implementation uses type_traits/alignment_of.hpp and type_traits/type_with_alignment.hpp


    @@ -940,8 +1404,7 @@ and type_traits/type_with_alignment.hpp

    Eric Friedman helped understand the issues involved with aligned storage, move/copy operations and exception safety.
    Many others have participated with useful comments: Aleksey Gurotov, Kevlin - Henney, David Abrahams, and others I can't recall. -

    + Henney, David Abrahams, and others I can't recall.

    Post-formal review:

    @@ -961,22 +1424,20 @@ API on top of variant<T,nil_t>.
    Dave Gomboc explained the meaning and usage of the Haskell analog to optional<>: the Maybe type constructor (analogy originally pointed out by David Sankel).
    Other comments were posted by Vincent Finn, Anthony Williams, Ed Brey, Rob Stewart, -and others. -

    +and others.
    +Joel de Guzman made the case for the support of references and helped with the +proper semantics.


    -

    Revised January 20, 2003

    +

    Revised December 3, 2003

    © Copyright boost.org 2003. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.

    Developed by Fernando Cacciola, the latest version of this file can be found at www.boost.org, and the boost discussion list at -www.yahoogroups.com/list/boost. -

    +HREF="http://www.boost.org">www.boost.org, and the boost discussion list at www.yahoogroups.com/list/boost.

    - - + \ No newline at end of file