Optional's Assignment fixed

[SVN r28412]
This commit is contained in:
Fernando Cacciola
2005-04-22 13:28:34 +00:00
parent 361d945b5c
commit 9a0013d668
7 changed files with 479 additions and 218 deletions

View File

@ -20,6 +20,7 @@ HREF="../../../boost/optional/optional.hpp">boost/optional/optional.hpp</A>&gt;
<DT><A HREF="#semantics">Semantics</A></DT> <DT><A HREF="#semantics">Semantics</A></DT>
<DT><A HREF="#examples">Examples</A></DT> <DT><A HREF="#examples">Examples</A></DT>
<DT><A HREF="#ref">Optional references</A></DT> <DT><A HREF="#ref">Optional references</A></DT>
<DT><A HREF="#refassign">Rebinding semantics for assignment of optional references</A></DT>
<DT><A HREF="#inplace">In-Place Factories</A></DT> <DT><A HREF="#inplace">In-Place Factories</A></DT>
<DT><A HREF="#bool">A note about optional&lt;bool&gt;</A></DT> <DT><A HREF="#bool">A note about optional&lt;bool&gt;</A></DT>
<DT><A HREF="#exsafety">Exception Safety Guarantees</A></DT> <DT><A HREF="#exsafety">Exception Safety Guarantees</A></DT>
@ -100,11 +101,12 @@ if ( p.second )
and neither default nor value initialization applies, it is said that the object is and neither default nor value initialization applies, it is said that the object is
<i><b>uninitialized</b></i>. Its actual value exist but has an <i><b>uninitialized</b></i>. Its actual value exist but has an
<i>indeterminate inital value</i> (c.f. 8.5.9).<br> <i>indeterminate inital value</i> (c.f. 8.5.9).<br>
<code>optional&lt;T&gt;</code> intends to formalize the notion of initialization/no-initialization <code>optional&lt;T&gt;</code> intends to formalize the notion of initialization
(or loack of it)
allowing a program to test whether an object has been initialized and stating that access to allowing a program to test whether an object has been initialized and stating that access to
the value of an uninitialized object is undefined behaviour. That is, the value of an uninitialized object is undefined behaviour. That is,
when a variable is declared as optional&lt;T&gt; and no initial value is given, when a variable is declared as optional&lt;T&gt; and no initial value is given,
the variable is formally uninitialized. A formally uninitialized optional object has conceptually the variable is <i>formally</i> uninitialized. A formally uninitialized optional object has conceptually
no value at all and this situation can be tested at runtime. It is formally <i>undefined behaviour</i> no value at all and this situation can be tested at runtime. It is formally <i>undefined behaviour</i>
to try to access the value of an uninitialized optional. An uninitialized optional can be <i>assigned</i> a value, in which case its initialization state changes to initialized. Furthermore, given the formal to try to access the value of an uninitialized optional. An uninitialized optional can be <i>assigned</i> 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 <i>uninitialized</i>.</P> treatment of initialization states in optional objects, it is even possible to reset an optional to <i>uninitialized</i>.</P>
@ -122,8 +124,8 @@ if ( p.second )
Using the <a href="../../../doc/html/variant.html">Boost.Variant</a> library, this model can be implemented Using the <a href="../../../doc/html/variant.html">Boost.Variant</a> library, this model can be implemented
in terms of <code>boost::variant&lt;T,nil_t&gt;</code>.<br> in terms of <code>boost::variant&lt;T,nil_t&gt;</code>.<br>
There is precedence for a discriminated union as a model for an optional value: the There is precedence for a discriminated union as a model for an optional value: the
<a href="http://www.haskell.org/"><u>Haskell</u></a> <b>Maybe</b> builtin type constructor, <a href="http://www.haskell.org/"><u>Haskell</u></a> <b>Maybe</b> builtin type constructor.
thus a discriminated union <code>T+nil_t</code> serves as a conceptual foundation.</p> Thus, a discriminated union <code>T+nil_t</code> serves as a conceptual foundation.</p>
<p>A <code>variant&lt;T,nil_t&gt;</code> follows naturally from the traditional idiom of extending <p>A <code>variant&lt;T,nil_t&gt;</code> follows naturally from the traditional idiom of extending
the range of possible values adding an additional sentinel value with the special meaning of <i>Nothing. </i> the range of possible values adding an additional sentinel value with the special meaning of <i>Nothing. </i>
However, this additional <i>Nothing</i> value is largely irrelevant for our purpose However, this additional <i>Nothing</i> value is largely irrelevant for our purpose
@ -180,33 +182,31 @@ object.</p>
<p><b>Direct Value Construction via copy:</b> To introduce a formally <p><b>Direct Value Construction via copy:</b> To introduce a formally
initialized wrapped object whose value is obtained as a copy of some object.</p> initialized wrapped object whose value is obtained as a copy of some object.</p>
<p><b>Deep Copy Construction:</b> To obtain a different yet equivalent wrapped <p><b>Deep Copy Construction:</b> To obtain a new yet equivalent wrapped
object.</p> object.</p>
<p><b>Direct Value Assignment (upon initialized):</b> To assign the wrapped object a value obtained <p><b>Direct Value Assignment (upon initialized):</b> To assign a value to the wrapped object.</p>
as a copy of some object.</p>
<p><b>Direct Value Assignment (upon uninitialized):</b> To initialize the wrapped object <p><b>Direct Value Assignment (upon uninitialized):</b> To initialize the wrapped object
with a value obtained with a value obtained
as a copy of some object.</p> as a copy of some object.</p>
<p><b>Assignnment (upon initialized):</b> To assign the wrapped object a value obtained as a copy <p><b>Assignnment (upon initialized):</b> To assign to the wrapped object the value
of another wrapper's object.</p> of another wrapped object.</p>
<p><b>Assignnment (upon uninitialized):</b> To initialize the wrapped object <p><b>Assignnment (upon uninitialized):</b> To initialize the wrapped object
with value obtained as a copy with value of another wrapped object.</p>
of another wrapper's object.</p>
<p><b>Deep Relational Operations (when supported by the type T):</b> To compare <p><b>Deep Relational Operations (when supported by the type T):</b> To compare
wrapped object values taking into account the presence of uninitialized wrapped object values taking into account the presence of uninitialized
operands.</p> states.</p>
<p><b>Value access:</b> To unwrap the wrapped object.</p> <p><b>Value access:</b> To unwrap the wrapped object.</p>
<p><b>Initialization state query:</b> To determine if the object is formally <p><b>Initialization state query:</b> To determine if the object is formally
initialized or not.</p> initialized or not.</p>
<p><b>Swap:</b> To exchange wrapper's objects. (with whatever exception safety <p><b>Swap:</b> To exchange wrapped objects. (with whatever exception safety
guarantiees are provided by T's swap).</p> guarantiees are provided by T's swap).</p>
<p><b>De-initialization:</b> To release the wrapped object (if any) and leave <p><b>De-initialization:</b> To release the wrapped object (if any) and leave
@ -313,7 +313,7 @@ class optional
optional () ; optional () ;
optional ( detail::none_t ) ; optional ( none_t ) ;
optional ( T const&amp; v ) ; optional ( T const&amp; v ) ;
@ -325,7 +325,7 @@ class optional
template&lt;class TypedInPlaceFactory&gt; explicit optional ( TypedInPlaceFactory const&amp; f ) ; template&lt;class TypedInPlaceFactory&gt; explicit optional ( TypedInPlaceFactory const&amp; f ) ;
optional&amp; operator = ( detail::none_t ) ; optional&amp; operator = ( none_t ) ;
optional&amp; operator = ( T const&amp; v ) ; optional&amp; operator = ( T const&amp; v ) ;
@ -427,7 +427,7 @@ assert ( !def ) ;</pre>
<HR> <HR>
<pre>optional&lt;T&gt;::optional( detail::none_t );</pre> <pre>optional&lt;T&gt;::optional( none_t );</pre>
<blockquote> <blockquote>
<p><b>Effect:</b> Constructs an <b>optional </b>uninitialized.</p> <p><b>Effect:</b> Constructs an <b>optional </b>uninitialized.</p>
<p><b>Postconditions:</b> <b>*this</b> is <u>uninitialized</u>.</p> <p><b>Postconditions:</b> <b>*this</b> is <u>uninitialized</u>.</p>
@ -436,11 +436,12 @@ assert ( !def ) ;</pre>
<blockquote> <blockquote>
<p>T's default constructor <u><i>is not</i></u> called.<br> <p>T's default constructor <u><i>is not</i></u> called.<br>
The The
expression <code>boost::none</code> denotes an instance of <code>boost::detail::none_t</code> that can be expression <code>boost::none</code> denotes an instance of <code>boost::none_t</code> that can be
used as the parameter.</p> used as the parameter.</p>
</blockquote> </blockquote>
<p><b>Example:</b></p> <p><b>Example:</b></p>
<blockquote> <blockquote>
<pre>#include &lt;boost/none.hpp&gt;</pre>
<pre>optional&lt;T&gt; n(none) ; <pre>optional&lt;T&gt; n(none) ;
assert ( !n ) ;</pre> assert ( !n ) ;</pre>
</blockquote> </blockquote>
@ -468,7 +469,7 @@ assert ( *opt == v ) ;</pre>
<HR> <HR>
<pre>optional&lt;T&amp;&gt;::optional( T ref )</pre> <pre>optional&lt;T&amp;&gt;::optional( T&amp; ref )</pre>
<blockquote> <blockquote>
<p><b>Effect:</b> Directly-Constructs an <b>optional</b>.</p> <p><b>Effect:</b> Directly-Constructs an <b>optional</b>.</p>
<p><b>Postconditions:</b> <b>*this</b> is <u>initialized</u> and its value is an <p><b>Postconditions:</b> <b>*this</b> is <u>initialized</u> and its value is an
@ -522,12 +523,11 @@ assert ( init2 == init ) ;
<blockquote> <blockquote>
<p><b>Effect:</b> Copy-Constructs an <b>optional</b>.</p> <p><b>Effect:</b> Copy-Constructs an <b>optional</b>.</p>
<p><b>Postconditions:</b> If <b>rhs</b> is initialized, <b>*this</b> is initialized <p><b>Postconditions:</b> If <b>rhs</b> is initialized, <b>*this</b> is initialized
and its value is a <i>copy</i> of the internal wrapper holding the references in <b>rhs</b>; else <b>*this</b> and its value is another reference to the same object referenced by <b>*rhs</b>; else <b>*this</b>
is uninitialized.</p> is uninitialized.</p>
<p><b>Throws:</b> Nothing.</p> <p><b>Throws:</b> Nothing.</p>
<p><b>Notes:</b> If <b>rhs</b> is initialized, the internal wrapper will be <p><b>Notes:</b> If <b>rhs</b> is initialized, both <b>*this</b> and <b>*rhs</b> will
copied and just like true references, both <b>*this</b> and <b>rhs</b> will refeer to the same object<b> </b>(they alias).</p>
referr to the same object<b> </b>(will alias).</p>
<p><b>Example:</b></p> <p><b>Example:</b></p>
<blockquote> <blockquote>
<pre>optional&lt;T&amp;&gt; uninit ; <pre>optional&lt;T&amp;&gt; uninit ;
@ -542,6 +542,13 @@ assert ( *init == v ) ;
optional&lt;T&gt; init2 ( init ) ; optional&lt;T&gt; init2 ( init ) ;
assert ( *init2 == v ) ; assert ( *init2 == v ) ;
v = 3 ;
assert ( *init == 3 ) ;
assert ( *init2 == 3 ) ;
</pre> </pre>
</blockquote> </blockquote>
@ -615,22 +622,55 @@ assert ( *y == v ) ;
<p><b>Effect:</b> Assigns the value 'rhs' to an <b>optional</b>.</p> <p><b>Effect:</b> Assigns the value 'rhs' to an <b>optional</b>.</p>
<p><b>Postconditions:</b> <b>*this</b> is initialized <p><b>Postconditions:</b> <b>*this</b> is initialized
and its value is a <i>copy</i> of <b>rhs.</b></p> and its value is a <i>copy</i> of <b>rhs.</b></p>
<p><b>Throws:</b> Whatever T::T( T const& ) throws.</p> <p><b>Throws:</b> Whatever T::operator=( T const& ) or T::T(T conbst&amp;) throws.</p>
<p><b>Notes:</b> If <b>*this</b> was initialized, it is first reset to uninitialized <p><b>Notes:</b> If <b>*this</b> was initialized, T's assignment operator is
using T::~T(), then T::T(<b>rhs</b>) is called.</p> used, otherwise, its copy-contructor is used.</p>
<p><b>Exception Safety:</b> <u>Basic:</u> Exceptions can only be thrown during T::T( T const& ); <p><b>Exception Safety:</b> In the event of an exception, the initialization
in that case, <b>*this</b> is left <u>uninitialized</u>. state of <b>*this</b> is unchanged and its value unspecified as far as optional
</p> is concerned (it is up to T's operator=()) [If <b>*this</b> is initially
uninitialized and T's <i>copy constructor</i> fails, <b>*this</b> is left
properly uninitialized]</p>
<p><b>Example:</b></p> <p><b>Example:</b></p>
<blockquote> <blockquote>
<pre>T x; <pre>T x;
optional&lt;T&gt; def ;
optional&lt;T&gt; opt(x) ; optional&lt;T&gt; opt(x) ;
T y; T y;
def = y ;
assert ( *def == y ) ;
opt = y ; opt = y ;
assert ( *opt == y ) ; assert ( *opt == y ) ;</pre>
// previous value (copy of 'v') destroyed from within 'opt'. </blockquote>
</blockquote>
<HR>
<pre>optional&lt;T&amp;&gt;&amp; optional&lt;T&amp;&gt;::operator= ( T&amp; const&amp; rhs ) ;</pre>
<blockquote>
<p><b>Effect:</b> (Re)binds thee wrapped reference.</p>
<p><b>Postconditions:</b> <b>*this</b> is initialized
and it references the same object referenced by <b>rhs.</b></p>
<p><b>Notes:</b> If <b>*this</b> was initialized, is is <i>rebound</i> to the
new object. See <A HREF="#refassign">here</a> for details on this behaviour.</p>
<p><b>Example:</b></p>
<blockquote>
<pre>int a = 1 ;
int b = 2 ;
T&amp; ra = a ;
T&amp; rb = b ;
optional&lt;int&amp;&gt; def ;
optional&lt;int&amp;&gt; opt(ra) ;
def = rb ; // binds 'def' to 'b' through 'rb'
assert ( *def == b ) ;
*def = a ; // changes the value of 'b' to a copy of the value of 'a'
assert ( b == a ) ;
int c = 3;
int&amp; rc = c ;
opt = rc ; // REBINDS to 'c' through 'rc'
c = 4 ;
assert ( *opt == 4 ) ;
</pre> </pre>
</blockquote> </blockquote>
</blockquote> </blockquote>
@ -644,21 +684,25 @@ assert ( *opt == y ) ;
and its value is a <i>copy</i> of the value of <b>rhs</b>; else <b>*this</b> and its value is a <i>copy</i> of the value of <b>rhs</b>; else <b>*this</b>
is uninitialized. is uninitialized.
</p> </p>
<p><b>Throws:</b> Whatever T::T( T const& ) throws.</p> <p><b>Throws:</b> Whatever T::operator( T const&amp;) or&nbsp; T::T( T const& ) throws.</p>
<p><b>Notes:</b> If <b>*this</b> was initialized, it is first reset to uninitialized <p><b>Notes:</b> If both<b> *this</b> and <b>rhs</b> are initially initialized,
using T::~T(), then T::T( T const& ) is called if <b>rhs</b> is initialized. T's <i>assignment</i> <i>operator</i> is used. If <b>*this</b> is initially initialized but <b>
</p> rhs</b> is uinitialized, T's <i>destructor</i> is called. If <b>*this</b> is initially
<p><b>Exception Safety:</b> <u>Basic:</u> Exceptions can only be thrown during T::T( T const& ); uninitialized but rhs is initialized, T's <i>copy constructor</i> is called.
in that case, <b>*this</b> is left <u>uninitialized</u>.
</p> </p>
<p><b>Exception Safety:</b> In the event of an exception, the initialization
state of <b>*this</b> is unchanged and its value unspecified as far as optional
is concerned (it is up to T's operator=()) [If <b>*this</b> is initially
uninitialized and T's <i>copy constructor</i> fails, <b>*this</b> is left
properly uninitialized]</p>
<p><b>Example:</b></p> <p><b>Example:</b></p>
<blockquote> <blockquote>
<pre>T v; <pre>T v;
optional&lt;T&gt; opt(v); optional&lt;T&gt; opt(v);
optional&lt;T&gt; uninit ; optional&lt;T&gt; def ;
opt = uninit ; opt = def ;
assert ( !opt ) ; assert ( !def ) ;
// previous value (copy of 'v') destroyed from within 'opt'. // previous value (copy of 'v') destroyed from within 'opt'.
</pre> </pre>
@ -667,6 +711,40 @@ assert ( !opt ) ;
<HR> <HR>
<pre>optional&lt;T&amp;&gt; &amp; optional&lt;T&amp;&gt;::operator= ( optional&lt;T&amp;&gt; const&amp; rhs ) ;</pre>
<blockquote>
<p><b>Effect:</b> (Re)binds thee wrapped reference.</p>
<p><b>Postconditions:</b> If <b>*rhs</b> is initialized, *<b>this</b> is initialized
and it references the same object referenced by <b>*rhs</b>; otherwise, <b>*this</b>
is uninitialized (and references no object).</p>
<p><b>Notes:</b> If <b>*this</b> was initialized and so is <b>*rhs</b>, <b>this</b>
is is <i>rebound</i> to the new object. See <A HREF="#refassign">here</a> for details on this behaviour.</p>
<p><b>Example:</b></p>
<blockquote>
<pre>int a = 1 ;
int b = 2 ;
T&amp; ra = a ;
T&amp; rb = b ;
optional&lt;int&amp;&gt; def ;
optional&lt;int&amp;&gt; ora(ra) ;
optional&lt;int&amp;&gt; orb(rb) ;
def = orb ; // binds 'def' to 'b' through 'rb' wrapped within 'orb'
assert ( *def == b ) ;
*def = ora ; // changes the value of 'b' to a copy of the value of 'a'
assert ( b == a ) ;
int c = 3;
int&amp; rc = c ;
optional&lt;int&amp;&gt; orc(rc) ;
ora = orc ; // REBINDS ora to 'c' through 'rc'
c = 4 ;
assert ( *ora == 4 ) ;
</pre>
</blockquote>
</blockquote>
<HR>
<pre>template&lt;U&gt; optional&amp; optional&lt;T <i>(not a ref)</i>&gt;::operator= ( optional&lt;U&gt; const&amp; rhs ) ;</pre> <pre>template&lt;U&gt; optional&amp; optional&lt;T <i>(not a ref)</i>&gt;::operator= ( optional&lt;U&gt; const&amp; rhs ) ;</pre>
<blockquote> <blockquote>
<p><b>Effect:</b> Assigns another <i>convertible</i> <b>optional</b> to an <b>optional</b>.</p> <p><b>Effect:</b> Assigns another <i>convertible</i> <b>optional</b> to an <b>optional</b>.</p>
@ -674,14 +752,17 @@ assert ( !opt ) ;
and its value is a <i>copy</i> of the value of <b>rhs</b> <i>converted</i> and its value is a <i>copy</i> of the value of <b>rhs</b> <i>converted</i>
to type T; else <b>*this</b> is uninitialized. to type T; else <b>*this</b> is uninitialized.
</p> </p>
<p><b>Throws:</b> Whatever T::T( U const& ) throws.</p> <p><b>Throws:</b> Whatever T::operator=( U const&amp; ) or T::T( U const& ) throws.</p>
<p><b>Notes:</b> If <b>*this</b> was initialized, it is first reset to uninitialized <p><b>Notes:</b> If both<b> *this</b> and <b>rhs</b> are initially initialized,
using T::~T(), then T::T( U const& ) is called if <b>rhs</b> is initialized, T's <i>assignment</i> <i>operator</i> (from U) is used. If <b>*this</b> is initially initialized but <b>
which requires a valid conversion from U to T. rhs</b> is uinitialized, T's <i>destructor</i> is called. If <b>*this</b> is initially
</p> uninitialized but rhs is initialized, T's <i>converting constructor</i> (from U) is called.
<p><b>Exception Safety:</b> <u>Basic:</u> Exceptions can only be thrown during T::T( U const& );
in that case, <b>*this</b> is left <u>uninitialized</u>.
</p> </p>
<p><b>Exception Safety:</b> In the event of an exception, the initialization
state of <b>*this</b> is unchanged and its value unspecified as far as optional
is concerned (it is up to T's operator=()) [If <b>*this</b> is initially
uninitialized and T's <i>converting constructor</i> fails, <b>*this</b> is left
properly uninitialized]</p>
<p><b>Example:</b></p> <p><b>Example:</b></p>
<blockquote> <blockquote>
<pre>T v; <pre>T v;
@ -1118,7 +1199,7 @@ class Fred
<HR> <HR>
<H2><A NAME="ref">Optional references</A></H2> <H2><A NAME="ref">Optional references</A></H2>
<p>This library allow the template parameter T to be of reference type: T&amp;, and <p>This library allows the template parameter T to be of reference type: T&amp;, and
to some extent, T const&amp;.</p> to some extent, T const&amp;.</p>
<p>However, since references are not real objects some restrictions apply and <p>However, since references are not real objects some restrictions apply and
@ -1141,6 +1222,85 @@ value, a true real reference is stored so aliasing will ocurr: </p>
than the reference itself.</li> than the reference itself.</li>
</ul> </ul>
<HR>
<h2><A NAME="refassign">Rebinding semantics for assignment of optional
references</a></h2>
<p>If you assign to an <i>uninitialized</i> optional&lt;T&amp;&gt; the effect is to bind (for the first time) to the object.
Clearly, there is no other choice.</p>
<pre>int x = 1 ;
int&amp; rx = x ;
optional&lt;int&amp;&gt; ora ;
optional&lt;int&amp;&gt; orb(x) ;
ora = orb ; // now 'ora'&nbsp;is bound to 'x' through 'rx'
*ora = 2 ; // Changes value of 'x' through 'ora'
assert(x==2);
</pre>
<p>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.</p>
<pre>int a = 1 ;
int&amp; ra = a ;
int b = 2 ;
int&amp; 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'
</pre>
<p>Now, if you assign to an <i>initialized</i> optional&lt;T&amp;&gt;, the effect is to <b>
rebind</b> to the new object instead of assigning the referee. This is unlike
bare C++ references.</p>
<pre>int a = 1 ;
int b = 2 ;
int&amp; ra = a ;
int&amp; rb = b ;
optional&lt;int&amp;&gt; ora(ra) ;
optional&lt;int&amp;&gt; orb(rb) ;
ora = orb ; // 'ora'&nbsp;is <b>rebound</b> to 'b'
*ora = 3 ; // Changes value of 'b' (not 'a')
assert(a==1);
assert(b==3);
</pre>
<h3>Rationale:</h3>
<p>Rebinding semantics for the assignment of <i>initialized</i> optional
references has been choosen to provide<b><i> </i>consistency among initialization
states<i> </i></b>even at the expense of lack of consistency with the semantics of bare
C++ references.<br>
It is true that optional&lt;U&gt; strives to behave as much as possible as U does
whenever it is initialized; but in the case when U is T&amp;, doing so would result
in incosistent behaviour w.r.t to the lvalue initialization state.</p>
<p>Imagine optional&lt;T&amp;&gt; fordwarding assignment to the referenced object (thus
changing the referenced object value but not rebinding), and consider the
following code :</p>
<pre>&nbsp; optional&lt;int&amp;&gt; a = get();
&nbsp; int x = 1 ;
&nbsp; int&amp; rx = x ;
&nbsp; optional&lt;int&amp;&gt; b(rx);
&nbsp; a = b ;
</pre>
<p>What does the assignment do?<br>
If 'a' is <i>uninitialized</i>, the answer is clear: it binds to 'x' (we now have
another reference to 'x').<br>
But what if 'a' is already <i>initiliazed? </i>it would change the value of the
referenced object (whatever that is); which is inconsistent with the other
possible case.</p>
<p>If optional&lt;T&amp;&gt; would assign just like T&amp; does, you would never be able to
use Optional's assignment without explicitely 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.</p>
<p>That is, you would have to discriminate in order to be consistency.<br>
<br>
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 <i>uninitialized</i> optional&lt;T&amp;&gt; 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.<br>
In such scenario, you can assign the value itself directly, as in:</p>
<pre>assert(!!opt);
*opt=value; </pre>
<HR> <HR>
<H2><A NAME="inplace">In-Place Factories</A></H2> <H2><A NAME="inplace">In-Place Factories</A></H2>
@ -1152,7 +1312,7 @@ type to be <a href="../../utility/CopyConstructible.html">Copy Constructible</a>
constructed object, often temporary, just to follow the copy from:</p> constructed object, often temporary, just to follow the copy from:</p>
<pre>struct X <pre>struct X
{ {
X ( int, std::string ) ; X ( int, std:::string ) ;
} ;</pre> } ;</pre>
<pre>class W <pre>class W
{ {
@ -1222,7 +1382,7 @@ public:
{ {
// Wrapped object constructed in-place via a TypedInPlaceFactory. // Wrapped object constructed in-place via a TypedInPlaceFactory.
// No temporary created. // No temporary created.
W ( TypedInPlaceFactory2&lt;X,int,std::string&gt;(123,"hello")) ; W ( TypedInPlaceFactory2&lt;X,int,std::string&rt;(123,"hello")) ;
} }
</pre> </pre>
<p>The factories are divided in two groups:<ul> <p>The factories are divided in two groups:<ul>
@ -1303,7 +1463,7 @@ of the assignment methods:</p>
InPlaceFactory const&amp; ) </code></li> InPlaceFactory const&amp; ) </code></li>
<li> <code>template&lt;class TypedInPlaceFactory&gt; optional&lt;T&gt;::operator= ( <li> <code>template&lt;class TypedInPlaceFactory&gt; optional&lt;T&gt;::operator= (
TypedInPlaceFactory const&amp; ) </code></li> TypedInPlaceFactory const&amp; ) </code></li>
<li> <code>optional&lt;T&gt;::reset ( T const&amp; )</code></li> <li> <code>optional&lt;T&gt;:::reset ( T const&amp;)</code></li>
</ul> </ul>
<p>Can only <i>guarantee</i> the <u>basic exception safety</u>: The lvalue optional is left <u>uninitialized</u> <p>Can only <i>guarantee</i> the <u>basic exception safety</u>: The lvalue optional is left <u>uninitialized</u>
if an exception is thrown (any previous value is <i>first</i> destroyed using T::~T())</p> if an exception is thrown (any previous value is <i>first</i> destroyed using T::~T())</p>
@ -1320,11 +1480,11 @@ for T::T ( T const&amp; ), you know that optional's assignment and reset has the
// Case 1: Exception thrown during assignment. // Case 1: Exception thrown during assignment.
// //
T v0(123); T v0(123);
optional&lt;T&gt opt0(v0); optional&ltT&gt opt0(v0);
try try
{ {
&nbsp; T v1(456); &nbsp; T v1(456);
&nbsp; optional&lt;T&gt opt1(v1); &nbsp; optional&ltT&gt opt1(v1);
&nbsp; opt0 = opt1 ; &nbsp; opt0 = opt1 ;
&nbsp; // If no exception was thrown, assignment succeeded. &nbsp; // If no exception was thrown, assignment succeeded.
@ -1340,7 +1500,7 @@ catch(...)
// Case 2: Exception thrown during reset(v) // Case 2: Exception thrown during reset(v)
// //
T v0(123); T v0(123);
optional&lt;T&gt opt(v0); optional&ltT&gt opt(v0);
try try
{ {
&nbsp; T v1(456); &nbsp; T v1(456);
@ -1432,8 +1592,8 @@ T <u>is not</u> required to be <a href="http://www.sgi.com/tech/stl/DefaultConst
</blockquote> </blockquote>
<HR> <HR>
<P>Revised Jannuary 30, 2004</P> <P>Revised April 21, 2005</P>
<p><EFBFBD> Copyright Fernando Luis Cacciola Carballal, 2003,2004</p> <p><EFBFBD> Copyright Fernando Luis Cacciola Carballal, 2003,2004,2005</p>
<p> Use, modification, and distribution are subject to the Boost Software <p> Use, modification, and distribution are subject to the Boost Software
License, Version 1.0. (See accompanying file <a href="../../../LICENSE_1_0.txt"> License, Version 1.0. (See accompanying file <a href="../../../LICENSE_1_0.txt">
LICENSE_1_0.txt</a> or copy at <a href="http://www.boost.org/LICENSE_1_0.txt"> LICENSE_1_0.txt</a> or copy at <a href="http://www.boost.org/LICENSE_1_0.txt">

View File

@ -12,7 +12,7 @@
#ifndef BOOST_NONE_17SEP2003_HPP #ifndef BOOST_NONE_17SEP2003_HPP
#define BOOST_NONE_17SEP2003_HPP #define BOOST_NONE_17SEP2003_HPP
#include "boost/detail/none_t.hpp" #include "boost/none_t.hpp"
// NOTE: Borland users have to include this header outside any precompiled headers // NOTE: Borland users have to include this header outside any precompiled headers
// (bcc<=5.64 cannot include instance data in a precompiled header) // (bcc<=5.64 cannot include instance data in a precompiled header)
@ -21,7 +21,7 @@ namespace boost {
namespace { namespace {
detail::none_t const none = ((detail::none_t)0) ; none_t const none = ((none_t)0) ;
} }

24
include/boost/none_t.hpp Normal file
View File

@ -0,0 +1,24 @@
// Copyright (C) 2003, Fernando Luis Cacciola Carballal.
//
// Use, modification, and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// See http://www.boost.org/lib/optional for documentation.
//
// You are welcome to contact the author at:
// fernando_cacciola@hotmail.com
//
#ifndef BOOST_NONE_T_17SEP2003_HPP
#define BOOST_NONE_T_17SEP2003_HPP
namespace boost {
namespace detail { struct none_helper{}; }
typedef int detail::none_helper::*none_t ;
} // namespace boost
#endif

View File

@ -26,7 +26,7 @@
#include "boost/mpl/bool.hpp" #include "boost/mpl/bool.hpp"
#include "boost/mpl/not.hpp" #include "boost/mpl/not.hpp"
#include "boost/detail/reference_content.hpp" #include "boost/detail/reference_content.hpp"
#include "boost/detail/none_t.hpp" #include "boost/none_t.hpp"
#include "boost/utility/compare_pointees.hpp" #include "boost/utility/compare_pointees.hpp"
#if BOOST_WORKAROUND(BOOST_MSVC, == 1200) #if BOOST_WORKAROUND(BOOST_MSVC, == 1200)
@ -167,7 +167,7 @@ class optional_base : public optional_tag
// Creates an optional<T> uninitialized. // Creates an optional<T> uninitialized.
// No-throw // No-throw
optional_base ( detail::none_t const& ) optional_base ( none_t const& )
: :
m_initialized(false) {} m_initialized(false) {}
@ -208,32 +208,40 @@ class optional_base : public optional_tag
~optional_base() { destroy() ; } ~optional_base() { destroy() ; }
// Assigns from another optional<T> (deep-copies the rhs value) // Assigns from another optional<T> (deep-copies the rhs value)
// Basic Guarantee: If T::T( T const& ) throws, this is left UNINITIALIZED
void assign ( optional_base const& rhs ) void assign ( optional_base const& rhs )
{ {
destroy(); if (is_initialized())
{
if ( rhs.is_initialized() )
assign_value(rhs.get_impl(), is_reference_predicate() );
else destroy();
}
else
{
if ( rhs.is_initialized() ) if ( rhs.is_initialized() )
construct(rhs.get_impl()); construct(rhs.get_impl());
} }
}
// Assigns from a T (deep-copies the rhs value) // Assigns from a T (deep-copies the rhs value)
// Basic Guarantee: If T::( T const& ) throws, this is left UNINITIALIZED
void assign ( argument_type val ) void assign ( argument_type val )
{ {
destroy(); if (is_initialized())
construct(val); assign_value(val, is_reference_predicate() );
else construct(val);
} }
// Assigns from "none", destroying the current value, if any, leaving this UNINITIALIZED // Assigns from "none", destroying the current value, if any, leaving this UNINITIALIZED
// No-throw (assuming T::~T() doesn't) // No-throw (assuming T::~T() doesn't)
void assign ( detail::none_t const& ) { destroy(); } void assign ( none_t const& ) { destroy(); }
#ifndef BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT #ifndef BOOST_OPTIONAL_NO_INPLACE_FACTORY_SUPPORT
template<class Expr> template<class Expr>
void assign_expr ( Expr const& expr, Expr const* tag ) void assign_expr ( Expr const& expr, Expr const* tag )
{ {
destroy(); if (is_initialized())
construct(expr,tag); assign_expr_to_initialized(expr,tag);
else construct(expr,tag);
} }
#endif #endif
@ -244,7 +252,6 @@ class optional_base : public optional_tag
void reset() { destroy(); } void reset() { destroy(); }
// Replaces the current value -if any- with 'val' // Replaces the current value -if any- with 'val'
// Basic Guarantee: If T::T( T const& ) throws this is left UNINITIALIZED.
void reset ( argument_type val ) { assign(val); } void reset ( argument_type val ) { assign(val); }
// Returns a pointer to the value if this is initialized, otherwise, // Returns a pointer to the value if this is initialized, otherwise,
@ -281,6 +288,21 @@ class optional_base : public optional_tag
factory.apply(m_storage.address()) ; factory.apply(m_storage.address()) ;
m_initialized = true ; m_initialized = true ;
} }
template<class Expr>
void assign_expr_to_initialized ( Expr const& factory, in_place_factory_base const* tag )
{
destroy();
construct(factory,tag);
}
// Constructs in-place using the given typed factory
template<class Expr>
void assign_expr_to_initialized ( Expr const& factory, typed_in_place_factory_base const* tag )
{
destroy();
construct(factory,tag);
}
#endif #endif
// Constructs using any expression implicitely convertible to the single argument // Constructs using any expression implicitely convertible to the single argument
@ -294,6 +316,16 @@ class optional_base : public optional_tag
m_initialized = true ; m_initialized = true ;
} }
// Assigns using a form any expression implicitely convertible to the single argument
// of a T's assignment operator.
// Converting assignments of optional<T> from optional<U> uses this function with
// 'Expr' being of type 'U' and relying on a converting assignment of T from U.
template<class Expr>
void assign_expr_to_initialized ( Expr const& expr, void const* )
{
assign_value(expr, is_reference_predicate());
}
#ifdef BOOST_OPTIONAL_WEAK_OVERLOAD_RESOLUTION #ifdef BOOST_OPTIONAL_WEAK_OVERLOAD_RESOLUTION
// BCB5.64 (and probably lower versions) workaround. // BCB5.64 (and probably lower versions) workaround.
// The in-place factories are supported by means of catch-all constructors // The in-place factories are supported by means of catch-all constructors
@ -321,6 +353,9 @@ class optional_base : public optional_tag
} }
#endif #endif
void assign_value ( argument_type val, is_not_reference_tag ) { get_impl() = val; }
void assign_value ( argument_type val, is_reference_tag ) { construct(val); }
void destroy() void destroy()
{ {
if ( m_initialized ) if ( m_initialized )
@ -391,7 +426,7 @@ class optional : public optional_detail::optional_base<T>
// Creates an optional<T> uninitialized. // Creates an optional<T> uninitialized.
// No-throw // No-throw
optional( detail::none_t const& none_ ) : base(none_) {} optional( none_t const& none_ ) : base(none_) {}
// Creates an optional<T> initialized with 'val'. // Creates an optional<T> initialized with 'val'.
// Can throw if T::T(T const&) does // Can throw if T::T(T const&) does
@ -453,14 +488,7 @@ class optional : public optional_detail::optional_base<T>
template<class U> template<class U>
optional& operator= ( optional<U> const& rhs ) optional& operator= ( optional<U> const& rhs )
{ {
this->destroy(); // no-throw
if ( rhs.is_initialized() )
{
// An exception can be thrown here.
// It it happens, THIS will be left uninitialized.
this->assign(rhs.get()); this->assign(rhs.get());
}
return *this ; return *this ;
} }
#endif #endif
@ -485,7 +513,7 @@ class optional : public optional_detail::optional_base<T>
// Assigns from a "none" // Assigns from a "none"
// Which destroys the current value, if any, leaving this UNINITIALIZED // Which destroys the current value, if any, leaving this UNINITIALIZED
// No-throw (assuming T::~T() doesn't) // No-throw (assuming T::~T() doesn't)
optional& operator= ( detail::none_t const& none_ ) optional& operator= ( none_t const& none_ )
{ {
this->assign( none_ ) ; this->assign( none_ ) ;
return *this ; return *this ;
@ -607,62 +635,62 @@ bool operator >= ( optional<T> const& x, optional<T> const& y )
template<class T> template<class T>
inline inline
bool operator == ( optional<T> const& x, detail::none_t const& ) bool operator == ( optional<T> const& x, none_t const& )
{ return equal_pointees(x, optional<T>() ); } { return equal_pointees(x, optional<T>() ); }
template<class T> template<class T>
inline inline
bool operator < ( optional<T> const& x, detail::none_t const& ) bool operator < ( optional<T> const& x, none_t const& )
{ return less_pointees(x,optional<T>() ); } { return less_pointees(x,optional<T>() ); }
template<class T> template<class T>
inline inline
bool operator != ( optional<T> const& x, detail::none_t const& y ) bool operator != ( optional<T> const& x, none_t const& y )
{ return !( x == y ) ; } { return !( x == y ) ; }
template<class T> template<class T>
inline inline
bool operator > ( optional<T> const& x, detail::none_t const& y ) bool operator > ( optional<T> const& x, none_t const& y )
{ return y < x ; } { return y < x ; }
template<class T> template<class T>
inline inline
bool operator <= ( optional<T> const& x, detail::none_t const& y ) bool operator <= ( optional<T> const& x, none_t const& y )
{ return !( y < x ) ; } { return !( y < x ) ; }
template<class T> template<class T>
inline inline
bool operator >= ( optional<T> const& x, detail::none_t const& y ) bool operator >= ( optional<T> const& x, none_t const& y )
{ return !( x < y ) ; } { return !( x < y ) ; }
template<class T> template<class T>
inline inline
bool operator == ( detail::none_t const& x, optional<T> const& y ) bool operator == ( none_t const& x, optional<T> const& y )
{ return equal_pointees(optional<T>() ,y); } { return equal_pointees(optional<T>() ,y); }
template<class T> template<class T>
inline inline
bool operator < ( detail::none_t const& x, optional<T> const& y ) bool operator < ( none_t const& x, optional<T> const& y )
{ return less_pointees(optional<T>() ,y); } { return less_pointees(optional<T>() ,y); }
template<class T> template<class T>
inline inline
bool operator != ( detail::none_t const& x, optional<T> const& y ) bool operator != ( none_t const& x, optional<T> const& y )
{ return !( x == y ) ; } { return !( x == y ) ; }
template<class T> template<class T>
inline inline
bool operator > ( detail::none_t const& x, optional<T> const& y ) bool operator > ( none_t const& x, optional<T> const& y )
{ return y < x ; } { return y < x ; }
template<class T> template<class T>
inline inline
bool operator <= ( detail::none_t const& x, optional<T> const& y ) bool operator <= ( none_t const& x, optional<T> const& y )
{ return !( y < x ) ; } { return !( y < x ) ; }
template<class T> template<class T>
inline inline
bool operator >= ( detail::none_t const& x, optional<T> const& y ) bool operator >= ( none_t const& x, optional<T> const& y )
{ return !( x < y ) ; } { return !( x < y ) ; }
// //
@ -679,8 +707,9 @@ namespace optional_detail {
#endif #endif
// optional's swap: // optional's swap:
// If both are initialized, calls swap(T&, T&), with whatever exception guarantess are given there. // If both are initialized, calls swap(T&, T&). If this swap throws, both will remain initialized but their values are now unspecified.
// If only one is initialized, calls I.reset() and U.reset(*I), with the Basic Guarantee // If only one is initialized, calls U.reset(*I), THEN I.reset().
// If U.reset(*I) throws, both are left UNCHANGED (U is kept uinitialized and I is never reset)
// If both are uninitialized, do nothing (no-throw) // If both are uninitialized, do nothing (no-throw)
template<class T> template<class T>
inline inline
@ -688,12 +717,12 @@ void optional_swap ( optional<T>& x, optional<T>& y )
{ {
if ( !x && !!y ) if ( !x && !!y )
{ {
x.reset(*y); // Basic guarantee. x.reset(*y);
y.reset(); y.reset();
} }
else if ( !!x && !y ) else if ( !!x && !y )
{ {
y.reset(*x); // Basic guarantee. y.reset(*x);
x.reset(); x.reset();
} }
else if ( !!x && !!y ) else if ( !!x && !!y )

View File

@ -93,12 +93,14 @@ void test_basics( T const* )
check_value(ob,a,z); check_value(ob,a,z);
// Value-Assignment upon Initialized optional. // Value-Assignment upon Initialized optional.
// T::T ( T const& x ) is used // T::operator=( T const& x ) is used
set_pending_dtor( ARG(T) ) ; set_pending_assign( ARG(T) ) ;
set_pending_copy ( ARG(T) ) ; set_pending_copy ( ARG(T) ) ;
set_pending_dtor ( ARG(T) ) ;
ob = b ; ob = b ;
check_is_not_pending_dtor( ARG(T) ) ; check_is_not_pending_assign( ARG(T) ) ;
check_is_not_pending_copy( ARG(T) ) ; check_is_pending_copy ( ARG(T) ) ;
check_is_pending_dtor ( ARG(T) ) ;
check_initialized(ob); check_initialized(ob);
check_value(ob,b,z); check_value(ob,b,z);
@ -111,13 +113,14 @@ void test_basics( T const* )
check_value_const(oa2,a,z); check_value_const(oa2,a,z);
// Assignment // Assignment
// T::~T() is used to destroy previous value in ob. // T::operator= ( T const& x ) is used to copy new value.
// T::T ( T const& x ) is used to copy new value. set_pending_assign( ARG(T) ) ;
set_pending_dtor( ARG(T) ) ;
set_pending_copy ( ARG(T) ) ; set_pending_copy ( ARG(T) ) ;
set_pending_dtor ( ARG(T) ) ;
oa = ob ; oa = ob ;
check_is_not_pending_dtor( ARG(T) ) ; check_is_not_pending_assign( ARG(T) ) ;
check_is_not_pending_copy( ARG(T) ) ; check_is_pending_copy ( ARG(T) ) ;
check_is_pending_dtor ( ARG(T) ) ;
check_initialized(oa); check_initialized(oa);
check_value(oa,b,z); check_value(oa,b,z);
@ -282,6 +285,9 @@ void test_throwing_direct_init( T const* )
BOOST_CHECK(!passed); BOOST_CHECK(!passed);
check_is_not_pending_copy( ARG(T) ); check_is_not_pending_copy( ARG(T) );
check_instance_count(count, ARG(T) ); check_instance_count(count, ARG(T) );
reset_throw_on_copy( ARG(T) ) ;
} }
// //
@ -317,6 +323,8 @@ void test_throwing_val_assign_on_uninitialized( T const* )
check_is_not_pending_copy( ARG(T) ); check_is_not_pending_copy( ARG(T) );
check_instance_count(count, ARG(T) ); check_instance_count(count, ARG(T) );
check_uninitialized(opt); check_uninitialized(opt);
reset_throw_on_copy( ARG(T) ) ;
} }
// //
@ -330,11 +338,10 @@ void test_throwing_val_assign_on_initialized( T const* )
T z(0); T z(0);
T a(8); T a(8);
T b(9); T b(9);
T x(-1);
int count = get_instance_count( ARG(T) ) ; int count = get_instance_count( ARG(T) ) ;
reset_throw_on_copy( ARG(T) ) ;
optional<T> opt ( b ) ; optional<T> opt ( b ) ;
++ count ; ++ count ;
@ -342,16 +349,16 @@ void test_throwing_val_assign_on_initialized( T const* )
check_value(opt,b,z); check_value(opt,b,z);
set_throw_on_copy( ARG(T) ) ; set_throw_on_assign( ARG(T) ) ;
bool passed = false ; bool passed = false ;
try try
{ {
// This should: // This should:
// Attempt to copy construct 'a' and throw. // Attempt to assign 'a' and throw.
// opt should be left uninitialized (even though it was initialized) // opt is kept initialized but its value not neccesarily fully assigned
set_pending_dtor( ARG(T) ) ; // (in this test, incompletely assigned is flaged with the value -1 being set)
set_pending_copy( ARG(T) ) ; set_pending_assign( ARG(T) ) ;
opt.reset ( a ) ; opt.reset ( a ) ;
passed = true ; passed = true ;
} }
@ -359,12 +366,12 @@ void test_throwing_val_assign_on_initialized( T const* )
BOOST_CHECK(!passed); BOOST_CHECK(!passed);
-- count ; check_is_not_pending_assign( ARG(T) );
check_is_not_pending_dtor( ARG(T) );
check_is_not_pending_copy( ARG(T) );
check_instance_count(count, ARG(T) ); check_instance_count(count, ARG(T) );
check_uninitialized(opt); check_initialized(opt);
check_value(opt,x,z);
reset_throw_on_assign ( ARG(T) ) ;
} }
// //
@ -378,8 +385,6 @@ void test_throwing_copy_initialization( T const* )
T z(0); T z(0);
T a(10); T a(10);
reset_throw_on_copy( ARG(T) ) ;
optional<T> opt (a); optional<T> opt (a);
int count = get_instance_count( ARG(T) ) ; int count = get_instance_count( ARG(T) ) ;
@ -406,6 +411,8 @@ void test_throwing_copy_initialization( T const* )
// Nothing should have happened to the source optional. // Nothing should have happened to the source optional.
check_initialized(opt); check_initialized(opt);
check_value(opt,a,z); check_value(opt,a,z);
reset_throw_on_copy( ARG(T) ) ;
} }
// //
@ -420,8 +427,6 @@ void test_throwing_assign_to_uninitialized( T const* )
T z(0); T z(0);
T a(11); T a(11);
reset_throw_on_copy( ARG(T) ) ;
optional<T> opt0 ; optional<T> opt0 ;
optional<T> opt1(a) ; optional<T> opt1(a) ;
@ -446,6 +451,8 @@ void test_throwing_assign_to_uninitialized( T const* )
check_is_not_pending_copy( ARG(T) ); check_is_not_pending_copy( ARG(T) );
check_instance_count(count, ARG(T) ); check_instance_count(count, ARG(T) );
check_uninitialized(opt0); check_uninitialized(opt0);
reset_throw_on_copy( ARG(T) ) ;
} }
// //
@ -460,24 +467,23 @@ void test_throwing_assign_to_initialized( T const* )
T z(0); T z(0);
T a(12); T a(12);
T b(13); T b(13);
T x(-1);
reset_throw_on_copy( ARG(T) ) ;
optional<T> opt0(a) ; optional<T> opt0(a) ;
optional<T> opt1(b) ; optional<T> opt1(b) ;
int count = get_instance_count( ARG(T) ) ; int count = get_instance_count( ARG(T) ) ;
set_throw_on_copy( ARG(T) ) ; set_throw_on_assign( ARG(T) ) ;
bool passed = false ; bool passed = false ;
try try
{ {
// This should: // This should:
// Attempt to copy construct 'opt1.value()' into opt0 and throw. // Attempt to copy construct 'opt1.value()' into opt0 and throw.
// opt0 should be left unmodified or uninitialized // opt0 is kept initialized but its value not neccesarily fully assigned
set_pending_dtor( ARG(T) ) ; // (in this test, incompletely assigned is flaged with the value -1 being set)
set_pending_copy( ARG(T) ) ; set_pending_assign( ARG(T) ) ;
opt0 = opt1 ; opt0 = opt1 ;
passed = true ; passed = true ;
} }
@ -486,11 +492,12 @@ void test_throwing_assign_to_initialized( T const* )
BOOST_CHECK(!passed); BOOST_CHECK(!passed);
// opt0 was left uninitialized // opt0 was left uninitialized
-- count ; check_is_not_pending_assign( ARG(T) );
check_is_not_pending_dtor( ARG(T) );
check_is_not_pending_copy( ARG(T) );
check_instance_count(count, ARG(T) ); check_instance_count(count, ARG(T) );
check_uninitialized(opt0); check_initialized(opt0);
check_value(opt0,x,z);
reset_throw_on_assign( ARG(T) ) ;
} }
// //
@ -505,8 +512,6 @@ void test_no_throwing_swap( T const* )
T a(14); T a(14);
T b(15); T b(15);
reset_throw_on_copy( ARG(T) ) ;
optional<T> def0 ; optional<T> def0 ;
optional<T> def1 ; optional<T> def1 ;
optional<T> opt0(a) ; optional<T> opt0(a) ;
@ -544,13 +549,12 @@ void test_throwing_swap( T const* )
T a(16); T a(16);
T b(17); T b(17);
T x(-1);
reset_throw_on_copy( ARG(T) ) ;
optional<T> opt0(a) ; optional<T> opt0(a) ;
optional<T> opt1(b) ; optional<T> opt1(b) ;
set_throw_on_copy( ARG(T) ) ; set_throw_on_assign( ARG(T) ) ;
// //
// Case 1: Both Initialized. // Case 1: Both Initialized.
@ -567,14 +571,18 @@ void test_throwing_swap( T const* )
BOOST_CHECK(!passed); BOOST_CHECK(!passed);
// Assuming swap(T&,T&) has at least the basic guarantee, these should hold. // optional's swap doesn't affect the initialized states of the arguments. Therefore,
BOOST_CHECK( ( !opt0 || ( !!opt0 && ( ( *opt0 == a ) || ( *opt0 == b ) ) ) ) ) ; // the following must hold:
BOOST_CHECK( ( !opt1 || ( !!opt1 && ( ( *opt1 == a ) || ( *opt1 == b ) ) ) ) ) ; check_initialized(opt0);
check_initialized(opt1);
check_value(opt0,x,a);
check_value(opt1,b,x);
// //
// Case 2: Only one Initialized. // Case 2: Only one Initialized.
// //
reset_throw_on_copy( ARG(T) ) ; reset_throw_on_assign( ARG(T) ) ;
opt0.reset(); opt0.reset();
opt1.reset(a); opt1.reset(a);
@ -585,7 +593,7 @@ void test_throwing_swap( T const* )
try try
{ {
// This should attempt to swap optionals and fail at opt0.reset(*opt1) // This should attempt to swap optionals and fail at opt0.reset(*opt1)
// opt0 should be left uninitialized and opt1 unchanged. // Both opt0 and op1 are left unchanged (unswaped)
swap(opt0,opt1); swap(opt0,opt1);
passed = true ; passed = true ;
@ -596,7 +604,9 @@ void test_throwing_swap( T const* )
check_uninitialized(opt0); check_uninitialized(opt0);
check_initialized(opt1); check_initialized(opt1);
check_value(opt1,a,b); check_value(opt1,a,x);
reset_throw_on_copy( ARG(T) ) ;
} }
// //
@ -607,8 +617,6 @@ void test_relops( T const* )
{ {
TRACE( std::endl << BOOST_CURRENT_FUNCTION ); TRACE( std::endl << BOOST_CURRENT_FUNCTION );
reset_throw_on_copy( ARG(T) ) ;
T v0(18); T v0(18);
T v1(19); T v1(19);
T v2(19); T v2(19);

View File

@ -92,11 +92,23 @@ class X
} }
X& operator= ( X const& rhs ) X& operator= ( X const& rhs )
{
pending_assign = false ;
if ( throw_on_assign )
{
TRACE ( "throwing exception in X's assignment" ) ;
v = -1 ;
throw 0 ;
}
else
{ {
v = rhs.v ; v = rhs.v ;
TRACE ( "X::operator =( X const& rhs). this=" << this << " rhs.v=" << rhs.v ) ; TRACE ( "X::operator =( X const& rhs). this=" << this << " rhs.v=" << rhs.v ) ;
}
return *this ; return *this ;
} }
@ -115,7 +127,9 @@ class X
static int count ; static int count ;
static bool pending_copy ; static bool pending_copy ;
static bool pending_dtor ; static bool pending_dtor ;
static bool pending_assign ;
static bool throw_on_copy ; static bool throw_on_copy ;
static bool throw_on_assign ;
private : private :
@ -130,27 +144,39 @@ class X
int X::count = 0 ; int X::count = 0 ;
bool X::pending_copy = false ; bool X::pending_copy = false ;
bool X::pending_dtor = false ; bool X::pending_dtor = false ;
bool X::pending_assign = false ;
bool X::throw_on_copy = false ; bool X::throw_on_copy = false ;
bool X::throw_on_assign = false ;
inline void set_pending_copy ( X const* x ) { X::pending_copy = true ; } inline void set_pending_copy ( X const* x ) { X::pending_copy = true ; }
inline void set_pending_dtor ( X const* x ) { X::pending_dtor = true ; } inline void set_pending_dtor ( X const* x ) { X::pending_dtor = true ; }
inline void set_pending_assign ( X const* x ) { X::pending_assign = true ; }
inline void set_throw_on_copy ( X const* x ) { X::throw_on_copy = true ; } inline void set_throw_on_copy ( X const* x ) { X::throw_on_copy = true ; }
inline void set_throw_on_assign ( X const* x ) { X::throw_on_assign = true ; }
inline void reset_throw_on_copy ( X const* x ) { X::throw_on_copy = false ; } inline void reset_throw_on_copy ( X const* x ) { X::throw_on_copy = false ; }
inline void reset_throw_on_assign ( X const* x ) { X::throw_on_assign = false ; }
inline void check_is_pending_copy ( X const* x ) { BOOST_CHECK( X::pending_copy ) ; } inline void check_is_pending_copy ( X const* x ) { BOOST_CHECK( X::pending_copy ) ; }
inline void check_is_pending_dtor ( X const* x ) { BOOST_CHECK( X::pending_dtor ) ; } inline void check_is_pending_dtor ( X const* x ) { BOOST_CHECK( X::pending_dtor ) ; }
inline void check_is_pending_assign ( X const* x ) { BOOST_CHECK( X::pending_assign ) ; }
inline void check_is_not_pending_copy ( X const* x ) { BOOST_CHECK( !X::pending_copy ) ; } inline void check_is_not_pending_copy ( X const* x ) { BOOST_CHECK( !X::pending_copy ) ; }
inline void check_is_not_pending_dtor ( X const* x ) { BOOST_CHECK( !X::pending_dtor ) ; } inline void check_is_not_pending_dtor ( X const* x ) { BOOST_CHECK( !X::pending_dtor ) ; }
inline void check_is_not_pending_assign( X const* x ) { BOOST_CHECK( !X::pending_assign ) ; }
inline void check_instance_count ( int c, X const* x ) { BOOST_CHECK( X::count == c ) ; } inline void check_instance_count ( int c, X const* x ) { BOOST_CHECK( X::count == c ) ; }
inline int get_instance_count ( X const* x ) { return X::count ; } inline int get_instance_count ( X const* x ) { return X::count ; }
inline void set_pending_copy (...) {} inline void set_pending_copy (...) {}
inline void set_pending_dtor (...) {} inline void set_pending_dtor (...) {}
inline void set_pending_assign (...) {}
inline void set_throw_on_copy (...) {} inline void set_throw_on_copy (...) {}
inline void set_throw_on_assign (...) {}
inline void reset_throw_on_copy (...) {} inline void reset_throw_on_copy (...) {}
inline void reset_throw_on_assign (...) {}
inline void check_is_pending_copy (...) {} inline void check_is_pending_copy (...) {}
inline void check_is_pending_dtor (...) {} inline void check_is_pending_dtor (...) {}
inline void check_is_pending_assign (...) {}
inline void check_is_not_pending_copy (...) {} inline void check_is_not_pending_copy (...) {}
inline void check_is_not_pending_dtor (...) {} inline void check_is_not_pending_dtor (...) {}
inline void check_is_not_pending_assign(...) {}
inline void check_instance_count (...) {} inline void check_instance_count (...) {}
inline int get_instance_count (...) { return 0 ; } inline int get_instance_count (...) { return 0 ; }

View File

@ -104,9 +104,16 @@ void test_basics( T const* )
T z(0); T z(0);
T original_a(1);
T a(1); T a(1);
T b(2);
T c(10);
T& aref = a ; T& aref = a ;
T& bref = b ;
// Default construction. // Default construction.
// 'def' state is Uninitialized. // 'def' state is Uninitialized.
@ -115,13 +122,16 @@ void test_basics( T const* )
check_ref_uninitialized(def); check_ref_uninitialized(def);
// Direct initialization. // Direct initialization.
// 'oa' state is Initialized with 'a' // 'oa' state is Initialized and binds to 'a'
// T::T( T const& x ) is NOT used becasue the optional holds a reference. // T::T( T const& x ) is NOT used becasue the optional holds a reference.
set_pending_copy( ARG(T) ) ; set_pending_copy( ARG(T) ) ;
optional<T&> oa ( aref ) ; optional<T&> oa ( aref ) ;
check_is_pending_copy( ARG(T) ); check_is_pending_copy( ARG(T) );
check_ref_initialized(oa); check_ref_initialized(oa);
check_ref_value(oa,a,z); check_ref_value(oa,a,z);
*oa = b ; // changes the value of 'a' through the reference
BOOST_CHECK( a == b ) ;
// Copy initialization. // Copy initialization.
// T::T ( T const& x ) is NOT used becasue the optional holds a reference. // T::T ( T const& x ) is NOT used becasue the optional holds a reference.
@ -130,27 +140,32 @@ void test_basics( T const* )
check_is_pending_copy( ARG(T) ) ; check_is_pending_copy( ARG(T) ) ;
check_ref_initialized_const(oa2); check_ref_initialized_const(oa2);
check_ref_value_const(oa2,a,z); check_ref_value_const(oa2,a,z);
*oa2 = original_a ; // restores the value of 'a' through the reference
BOOST_CHECK( a == original_a ) ;
T b(2);
optional<T&> ob ; optional<T&> ob ;
// Value-Assignment upon Uninitialized optional. // Value-Assignment upon Uninitialized optional.
// T::T ( T const& x ) is NOT used becasue the optional holds a reference. // T::T ( T const& x ) is NOT used becasue the optional holds a reference.
set_pending_copy( ARG(T) ) ; set_pending_copy( ARG(T) ) ;
ob = a ; ob = a ; // Binds ob to a temporary non-const refererence to 'a'
check_is_pending_copy( ARG(T) ) ; check_is_pending_copy( ARG(T) ) ;
check_ref_initialized(ob); check_ref_initialized(ob);
check_ref_value(ob,a,z); check_ref_value(ob,a,z);
a = c;
check_ref_value(ob,a,z);
// Value-Assignment upon Initialized optional. // Value-Assignment upon Initialized optional.
// T::T ( T const& x ) is NOT used becasue the optional holds a reference. // T::operator= ( T const& x ) is used.
set_pending_dtor( ARG(T) ) ; set_pending_assign( ARG(T) ) ;
set_pending_copy( ARG(T) ) ; ob = b ; // Rebinds 'ob' to 'b' (without changing 'a')
ob = b ; check_is_pending_assign( ARG(T) ) ;
check_is_pending_dtor( ARG(T) ) ;
check_is_pending_copy( ARG(T) ) ;
check_ref_initialized(ob); check_ref_initialized(ob);
check_ref_value(ob,b,z); check_ref_value(ob,b,z);
BOOST_CHECK(a == c); // From a=c in previous test
b = c;
check_ref_value(ob,b,z);
// Assignment initialization. // Assignment initialization.
// T::T ( T const& x ) is NOT used becasue the optional holds a reference. // T::T ( T const& x ) is NOT used becasue the optional holds a reference.
@ -162,14 +177,12 @@ void test_basics( T const* )
// Assignment // Assignment
// T::~T() is used to destroy previous value in ob. // T::operator=( T const& x ) is used.
// T::T ( T const& x ) is NOT used becasue the optional holds a reference. set_pending_assign( ARG(T) ) ;
set_pending_dtor( ARG(T) ) ; oa = ob ; // Rebinds 'a' to 'b'
set_pending_copy( ARG(T) ) ; check_is_pending_assign( ARG(T) ) ;
oa = ob ;
check_is_pending_dtor( ARG(T) ) ;
check_is_pending_copy( ARG(T) ) ;
check_ref_initialized(oa); check_ref_initialized(oa);
a = original_a ;
check_ref_value(oa,b,z); check_ref_value(oa,b,z);
// Uninitializing Assignment upon Initialized Optional // Uninitializing Assignment upon Initialized Optional
@ -190,6 +203,7 @@ void test_basics( T const* )
check_is_pending_copy( ARG(T) ) ; check_is_pending_copy( ARG(T) ) ;
check_ref_uninitialized(oa); check_ref_uninitialized(oa);
// Deinitialization of Initialized Optional // Deinitialization of Initialized Optional
// T::~T() is NOT used becasue the optional holds a reference. // T::~T() is NOT used becasue the optional holds a reference.
set_pending_dtor( ARG(T) ) ; set_pending_dtor( ARG(T) ) ;