| |
| [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] |
| |
| |