mirror of
				https://github.com/boostorg/optional.git
				synced 2025-11-04 01:31:57 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			373 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			373 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
 | 
						|
[section Optional references]
 | 
						|
 | 
						|
This library allows 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 reefer to the same object.
 | 
						|
* Value-access will actually provide access to the referenced object
 | 
						|
rather than the reference itself.
 | 
						|
 | 
						|
[endsect]
 | 
						|
 | 
						|
[section Rebinding semantics for assignment of optional references]
 | 
						|
 | 
						|
If you assign to an ['uninitialized ] `optional<T&>` the effect is to bind (for
 | 
						|
the first time) to the object. Clearly, there is no other choice.
 | 
						|
 | 
						|
    int x = 1 ;
 | 
						|
    int& rx = x ;
 | 
						|
    optional<int&> ora ;
 | 
						|
    optional<int&> orb(x) ;
 | 
						|
    ora = orb ; // now 'ora' is bound to 'x' through 'rx'
 | 
						|
    *ora = 2 ; // Changes value of 'x' through 'ora'
 | 
						|
    assert(x==2); 
 | 
						|
 | 
						|
If you assign to a bare C++ reference, the assignment is forwarded to the
 | 
						|
referenced object; it's value changes but the reference is never rebound.
 | 
						|
 | 
						|
    int a = 1 ;
 | 
						|
    int& ra = a ;
 | 
						|
    int b = 2 ;
 | 
						|
    int& rb = b ;
 | 
						|
    ra = rb ; // Changes the value of 'a' to 'b'
 | 
						|
    assert(a==b);
 | 
						|
    b = 3 ;
 | 
						|
    assert(ra!=b); // 'ra' is not rebound to 'b'
 | 
						|
 | 
						|
Now, if you assign to an ['initialized ] `optional<T&>`, the effect is to
 | 
						|
[*rebind] to the new object instead of assigning the referee. This is unlike
 | 
						|
bare C++ references.
 | 
						|
 | 
						|
    int a = 1 ;
 | 
						|
    int b = 2 ;
 | 
						|
    int& ra = a ;
 | 
						|
    int& rb = b ;
 | 
						|
    optional<int&> ora(ra) ;
 | 
						|
    optional<int&> orb(rb) ;
 | 
						|
    ora = orb ; // 'ora' is rebound to 'b'
 | 
						|
    *ora = 3 ; // Changes value of 'b' (not 'a')
 | 
						|
    assert(a==1); 
 | 
						|
    assert(b==3); 
 | 
						|
 | 
						|
[heading Rationale]
 | 
						|
 | 
						|
Rebinding semantics for the assignment of ['initialized ] `optional` 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 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();
 | 
						|
    int x = 1 ;
 | 
						|
    int& rx = x ;
 | 
						|
    optional<int&> b(rx);
 | 
						|
    a = b ;
 | 
						|
 | 
						|
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 ['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 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.
 | 
						|
 | 
						|
That is, you would have to discriminate in order to be consistency.
 | 
						|
 | 
						|
If in your code rebinding to another object is not an option, then is very
 | 
						|
likely that binding for the fist time isn't either. In such case, assignment
 | 
						|
to an ['uninitialized ] `optional<T&>` shall be prohibited. It is quite possible
 | 
						|
that in such scenario the precondition that the lvalue must be already
 | 
						|
initialized exist. If it doesn't, then binding for the first time is OK
 | 
						|
while rebinding is not which is IMO very unlikely.
 | 
						|
In such scenario, you can assign the value itself directly, as in:
 | 
						|
 | 
						|
    assert(!!opt);
 | 
						|
    *opt=value;
 | 
						|
 | 
						|
[endsect]
 | 
						|
 | 
						|
[section 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 scheme, 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 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 family 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>(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< class InPlaceFactory >
 | 
						|
        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__
 | 
						|
 | 
						|
[endsect]
 | 
						|
 | 
						|
[section 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__— 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 bar()
 | 
						|
    {
 | 
						|
        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 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).
 | 
						|
 | 
						|
[endsect]
 | 
						|
 | 
						|
[section Exception Safety Guarantees]
 | 
						|
 | 
						|
Because of the current implementation (see [link boost_optional.implementation_notes 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 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.
 | 
						|
    //
 | 
						|
    T v0(123);
 | 
						|
    optional<T> opt0(v0);
 | 
						|
    try
 | 
						|
    {
 | 
						|
        T v1(456);
 | 
						|
        optional<T> opt1(v1);
 | 
						|
        opt0 = opt1 ;
 | 
						|
 | 
						|
        // If no exception was thrown, assignment succeeded.
 | 
						|
        assert( *opt0 == v1 ) ;
 | 
						|
    }
 | 
						|
    catch(...)
 | 
						|
    {
 | 
						|
        // If any exception was thrown, 'opt0' is reset to uninitialized.
 | 
						|
        assert( !opt0 ) ;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Case 2: Exception thrown during reset(v)
 | 
						|
    //
 | 
						|
    T v0(123);
 | 
						|
    optional<T> opt(v0);
 | 
						|
    try
 | 
						|
    {
 | 
						|
        T v1(456);
 | 
						|
        opt.reset ( v1 ) ;
 | 
						|
 | 
						|
        // If no exception was thrown, reset succeeded.
 | 
						|
        assert( *opt == v1 ) ;
 | 
						|
    }
 | 
						|
    catch(...)
 | 
						|
    {
 | 
						|
        // If any exception was thrown, 'opt' is reset to uninitialized.
 | 
						|
        assert( !opt ) ;
 | 
						|
    }
 | 
						|
 | 
						|
[heading 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.
 | 
						|
 | 
						|
[endsect]
 | 
						|
 | 
						|
[section Type requirements]
 | 
						|
 | 
						|
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 __SGI_DEFAULT_CONSTRUCTIBLE__.
 | 
						|
 | 
						|
[endsect]
 | 
						|
 | 
						|
 |