[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` treats it wrapped pseudo-object much as a real value, a true real reference is stored so aliasing will ocurr: * Copies of `optional` 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` 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 ora ; optional 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; its 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`, 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 ora(ra) ; optional 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` 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` forwarding assignment to the referenced object (thus changing the referenced object value but not rebinding), and consider the following code: optional a = get(); int x = 1 ; int& rx = x ; optional 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` 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 consistent. If in your code rebinding to another object is not an option, then it is very likely that binding for the first time isn't either. In such case, assignment to an ['uninitialized ] `optional` shall be prohibited. It is quite possible that in such a scenario it is a precondition that the lvalue must be already initialized. If it isn't, then binding for the first time is OK while rebinding is not which is IMO very unlikely. In such a 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 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(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 InPlaceFactoryN in_place ( A0 const& a0, ..., AN const& aN) ; template TypedInPlaceFactoryN 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 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] `optional` 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`. It should be carefully considered if an `optional` 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` can lead to subtle errors due to the implicit `bool` conversion: void foo ( bool v ) ; void bar() { optional 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::operator= ( optional const& )` * `optional::operator= ( T const& )` * `template optional::operator= ( optional const& )` * `template optional::operator= ( InPlaceFactory const& )` * `template optional::operator= ( TypedInPlaceFactory const& ) ` * `optional::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::operator= ( detail::none_t )` * `optional::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 opt0(v0); try { T v1(456); optional 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 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&, optional& )` 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::reset( T const& )` (since `optional::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]