2007-05-29 06:40:25 +00:00
|
|
|
|
|
|
|
[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.
|
2009-01-28 09:14:56 +00:00
|
|
|
W ( TypedInPlaceFactory2<X,int,std::string>(123,"hello")) ;
|
2007-05-29 06:40:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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]
|
|
|
|
|
2009-02-12 14:01:48 +00:00
|
|
|
Because of the current implementation (see [link boost_optional.implementation_notes Implementation Notes]), all of the assignment methods:
|
2007-05-29 06:40:25 +00:00
|
|
|
|
|
|
|
* `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]
|
|
|
|
|
|
|
|
|