Compare commits

...

9 Commits

Author SHA1 Message Date
172b90d2b8 Added simple operators << and >> to optional<>
[SVN r32533]
2006-02-03 21:39:21 +00:00
e1ffaf38b6 Fixed the converting assignment bug in optional<>
Fixed the usage of 'None' in converter.h, which is declared as a macro in X11/X.h


[SVN r32531]
2006-02-03 19:56:03 +00:00
a6f4615d5f This commit was manufactured by cvs2svn to create branch 'RC_1_33_0'.
[SVN r30300]
2005-07-28 18:22:24 +00:00
b831e5f88d Typos fixed
[SVN r29952]
2005-07-08 23:22:29 +00:00
88d002a7be Doc fixes
[SVN r29158]
2005-05-23 14:04:03 +00:00
dda39772ad Fix doubled EOLs.
[SVN r28438]
2005-04-23 16:35:57 +00:00
ed504fae7b Converted to Unix format
[SVN r28432]
2005-04-23 02:59:05 +00:00
9a0013d668 Optional's Assignment fixed
[SVN r28412]
2005-04-22 13:28:34 +00:00
361d945b5c fixed couple of html-typos
[SVN r25985]
2004-10-30 20:34:18 +00:00
7 changed files with 533 additions and 239 deletions

View File

@ -3,7 +3,10 @@
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<meta http-equiv="Content-Language" content="en-us">
<meta name="GENERATOR" content="Microsoft FrontPage 5.0">
<meta name="ProgId" content="FrontPage.Editor.Document">
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
<LINK REL="stylesheet" TYPE="text/css" HREF="../../../boost.css">
<TITLE>Header </TITLE>
</HEAD>
@ -20,6 +23,7 @@ HREF="../../../boost/optional/optional.hpp">boost/optional/optional.hpp</A>&gt;
<DT><A HREF="#semantics">Semantics</A></DT>
<DT><A HREF="#examples">Examples</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="#bool">A note about optional&lt;bool&gt;</A></DT>
<DT><A HREF="#exsafety">Exception Safety Guarantees</A></DT>
@ -41,7 +45,7 @@ HREF="../../../boost/optional/optional.hpp">boost/optional/optional.hpp</A>&gt;
<P>There are different approaches to the issue of not having a value to return.</P>
<P>A typical approach is to consider the existence of a valid return value as
a postcondition, so that if the function cannot compute the value to return,
it has either undefined behavior (and can use asssert in a debug build)
it has either undefined behavior (and can use assert in a debug build)
or uses a runtime check and throws an exception if the postcondition is violated.
This is a reasonable choice for example, for function (A), because the
lack of a proper return value is directly related to an invalid parameter (out
@ -99,13 +103,15 @@ if ( p.second )
If the declaration uses an empty initializer (no initial value is given),
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>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
<i>indeterminate initial value</i> (c.f. 8.5.9).<br>
<code>optional&lt;T&gt;</code> intends to formalize the notion of initialization
(or lack of it)
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 behavior. That is,
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
no value at all and this situation can be tested at runtime. It is formally <i>undefined behaviour</i>
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 behavior</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
treatment of initialization states in optional objects, it is even possible to reset an optional to <i>uninitialized</i>.</P>
<P>In C++ there is no formal notion of uninitialized objects, which
@ -122,17 +128,18 @@ if ( p.second )
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>
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,
thus a discriminated union <code>T+nil_t</code> serves as a conceptual foundation.</p>
<a href="http://www.haskell.org/"><u>Haskell</u></a> <b>Maybe</b> built-in type constructor.
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
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
since our goal is to formalize the notion of uninitialized objects and, while a special extended value <i>can</i> be used to convey that meaning, it is not strictly neccesary in order to do so.</p>
since our goal is to formalize the notion of uninitialized objects and, while a special extended value <i>can</i> be used to convey that meaning, it is not strictly
necessary in order to do so.</p>
<p>The observation made in the last paragraph about the irrelevant nature of the additional <code>nil_t</code> with respect to
<u>purpose</u> of optional&lt;T&gt; suggests
an alternative model: a <i>container</i> that either has a value of T or nothing.
</p>
<p>As of this writting I don't know of any precedence for a variable-size fixed-capacity (of 1)
<p>As of this writing I don't know of any precedence for a variable-size fixed-capacity (of 1)
stack-based container model for optional values, yet I believe this is the consequence of
the lack of practical implementations of such a container rather than an inherent shortcoming
of the container model.</p>
@ -146,7 +153,8 @@ For instance, these models show the <i>exact</i> semantics required for a wrappe
<li>If the variant's current type is T, it is modeling an <i>initialized</i> optional.</li>
<li>If the variant's current type is not T, it is modeling an <i>uninitialized</i> optional.</li>
<li>Testing if the variant's current type is T models testing if the optional is initialized</li>
<li>Trying to extract a T from a variant when its current type is not T, models the undefined behaviour
<li>Trying to extract a T from a variant when its current type is not T, models the undefined
behavior
of trying to access the value of an uninitialized optional</li>
</blockquote>
<p>Single-element container:</p>
@ -156,7 +164,7 @@ of trying to access the value of an uninitialized optional</li>
<li>If the container is not empty (contains an object of type T), it is modeling an <i>initialized</i> optional.</li>
<li>If the container is empty, it is modeling an <i>uninitialized</i> optional.</li>
<li>Testing if the container is empty models testing if the optional is initialized</li>
<li>Trying to extract a T from an empty container models the undefined behaviour
<li>Trying to extract a T from an empty container models the undefined behavior
of trying to access the value of an uninitialized optional</li>
</blockquote>
@ -169,7 +177,8 @@ plus the additional semantics corresponding to this special state.<br>
As such, <code>optional&lt;T&gt;</code> could be thought of as a <i>supertype</i> of T. Of course,
we can't do that in C++, so we need to compose the desired semantics using a different mechanism.<br>
Doing it the other way around, that is, making <code>optional&lt;T&gt;</code> a <i>subtype</i> of T is not only
conceptually wrong but also impractical: it is not allowed to derive from a non-class type, such as a builtin type.</p>
conceptually wrong but also impractical: it is not allowed to derive from a non-class type, such as a
built-in type.</p>
<p>We can draw from the purpose of optional&lt;T&gt; the required basic semantics:</p>
@ -180,34 +189,32 @@ object.</p>
<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>
<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>
<p><b>Direct Value Assignment (upon initialized):</b> To assign the wrapped object a value obtained
as a copy of some object.</p>
<p><b>Direct Value Assignment (upon initialized):</b> To assign a value to the wrapped object.</p>
<p><b>Direct Value Assignment (upon uninitialized):</b> To initialize the wrapped object
with a value obtained
as a copy of some object.</p>
<p><b>Assignnment (upon initialized):</b> To assign the wrapped object a value obtained as a copy
of another wrapper's object.</p>
<p><b>Assignment (upon initialized):</b> To assign to the wrapped object the value
of another wrapped object.</p>
<p><b>Assignnment (upon uninitialized):</b> To initialize the wrapped object
with value obtained as a copy
of another wrapper's object.</p>
<p><b>Assignment (upon uninitialized):</b> To initialize the wrapped object
with value of another wrapped object.</p>
<p><b>Deep Relational Operations (when supported by the type T):</b> To compare
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>Initialization state query:</b> To determine if the object is formally
initialized or not.</p>
<p><b>Swap:</b> To exchange wrapper's objects. (with whatever exception safety
guarantiees are provided by T's swap).</p>
<p><b>Swap:</b> To exchange wrapped objects. (with whatever exception safety
guarantees are provided by T's swap).</p>
<p><b>De-initialization:</b> To release the wrapped object (if any) and leave
the wrapper in the uninitialized state.</p>
@ -234,7 +241,7 @@ those operations which are well defined (w.r.t the type T) even if any of the
operands are uninitialized. These operations include: construction,
copy-construction, assignment, swap and relational operations.<br>
For the value access operations, which are undefined (w.r.t the type T) when the
operand is uninitialized, a different interface is choosen (which will be
operand is uninitialized, a different interface is chosen (which will be
explained next).<br>
Also, the presence of the possibly uninitialized state requires additional
operations not provided by T itself which are supported by a special interface.</p>
@ -259,7 +266,7 @@ optional objects: The operators * and -&gt;</h3>
the implied pointee actually exist.</P>
<P>Such a <i>de facto</i> idiom for referring to optional objects can be formalized in the form of a
concept: the <a href="../../utility/OptionalPointee.html">OptionalPointee</a> concept.<br>
This concept captures the syntactic usage of operatos *, -> and conversion to bool to convey
This concept captures the syntactic usage of operators *, -> and conversion to bool to convey
the notion of optionality.</P>
<P>However, pointers are good to <u>refer</u> to optional objects, but not particularly good
to handle the optional objects in all other respects, such as initializing or moving/copying
@ -273,22 +280,22 @@ them. The problem resides in the shallow-copy of pointer semantics: if you need
allocation and use a smart pointer to automatically handle the details of this.
For example, if a function is to optionally return an object X, it can use shared_ptr&lt;X&gt;
as the return value. However, this requires dynamic allocation of X. If X is
a builtin or small POD, this technique is very poor in terms of required resources.
a built-in or small POD, this technique is very poor in terms of required resources.
Optional objects are essentially values so it is very convenient to be able to use automatic
storage and deep-copy semantics to manipulate optional values just as we do with ordinary
values. Pointers do not have this semantics, so are unappropriate for the initialization and
values. Pointers do not have this semantics, so are inappropriate for the initialization and
transport of optional values, yet are quite convenient for handling the access to the
possible undefined value because of the idiomatic aid present in the OptionalPointee
concept incarnated by pointers.
</p>
<h4>Optional&lt;T&gt; as a model of OptionalPointee</h4>
<P>For value access operations optional&lt;&gt; uses operators * and -&gt; to lexically
warn about the possibliy uninitialized state appealing to the familiar pointer
warn about the possibly uninitialized state appealing to the familiar pointer
semantics w.r.t. to null pointers.<br>
<u><b>However, it is particularly important to note that optional<> objects are not pointers. optional&lt;&gt;
is not, and does not model, a pointer</b></u><b>.</b>
<P>For instance, optional&lt;&gt; has not shallow-copy so does not alias: two different optionals
never refer to the <i>same</i> value unless T itself is an reference (but my have <i>equivalent</i> values).<br>
<P>For instance, optional&lt;&gt; does not have shallow-copy so does not alias: two different optionals
never refer to the <i>same</i> value unless T itself is a reference (but may have <i>equivalent</i> values).<br>
The difference between an optional&lt;T&gt; and a pointer must be kept in mind, particularly
because the semantics of relational operators are different: since optional&lt;T&gt;
is a value-wrapper, relational operators are deep: they compare optional values;
@ -313,7 +320,7 @@ class optional
optional () ;
optional ( detail::none_t ) ;
optional ( none_t ) ;
optional ( T const&amp; v ) ;
@ -325,7 +332,7 @@ class optional
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 ) ;
@ -427,7 +434,7 @@ assert ( !def ) ;</pre>
<HR>
<pre>optional&lt;T&gt;::optional( detail::none_t );</pre>
<pre>optional&lt;T&gt;::optional( none_t );</pre>
<blockquote>
<p><b>Effect:</b> Constructs an <b>optional </b>uninitialized.</p>
<p><b>Postconditions:</b> <b>*this</b> is <u>uninitialized</u>.</p>
@ -436,11 +443,12 @@ assert ( !def ) ;</pre>
<blockquote>
<p>T's default constructor <u><i>is not</i></u> called.<br>
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>
</blockquote>
<p><b>Example:</b></p>
<blockquote>
<pre>#include &lt;boost/none.hpp&gt;</pre>
<pre>optional&lt;T&gt; n(none) ;
assert ( !n ) ;</pre>
</blockquote>
@ -468,7 +476,7 @@ assert ( *opt == v ) ;</pre>
<HR>
<pre>optional&lt;T&amp;&gt;::optional( T ref )</pre>
<pre>optional&lt;T&amp;&gt;::optional( T&amp; ref )</pre>
<blockquote>
<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
@ -522,12 +530,11 @@ assert ( init2 == init ) ;
<blockquote>
<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
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>
<p><b>Throws:</b> Nothing.</p>
<p><b>Notes:</b> If <b>rhs</b> is initialized, the internal wrapper will be
copied and just like true references, both <b>*this</b> and <b>rhs</b> will
referr to the same object<b> </b>(will alias).</p>
<p><b>Notes:</b> If <b>rhs</b> is initialized, both <b>*this</b> and <b>*rhs</b> will
reefer to the same object<b> </b>(they alias).</p>
<p><b>Example:</b></p>
<blockquote>
<pre>optional&lt;T&amp;&gt; uninit ;
@ -542,6 +549,13 @@ assert ( *init == v ) ;
optional&lt;T&gt; init2 ( init ) ;
assert ( *init2 == v ) ;
v = 3 ;
assert ( *init == 3 ) ;
assert ( *init2 == 3 ) ;
</pre>
</blockquote>
@ -584,7 +598,7 @@ assert( *y == 123 ) ;
<p><b>Effect:</b> Constructs an <b>optional</b> with a value of T obtained from
the factory.</p>
<p><b>Postconditions:</b>&nbsp; <b>*this</b> is <u>initialized</u> and its value is
<i>directly given</i> from the factory 'f' (i.e, the value<u> is not copied</u>).</p>
<i>directly given</i> from the factory 'f' (i.e., the value<u> is not copied</u>).</p>
<p><b>Throws:</b> Whatever the T constructor called by the factory throws.</p>
<p><b>Notes:</b> See <A HREF="#inplace">In-Place Factories</A></p>
<p><b>Exception Safety:</b> Exceptions can only be thrown during the call to the
@ -615,22 +629,55 @@ assert ( *y == v ) ;
<p><b>Effect:</b> Assigns the value 'rhs' to an <b>optional</b>.</p>
<p><b>Postconditions:</b> <b>*this</b> is initialized
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>Notes:</b> If <b>*this</b> was initialized, it is first reset to uninitialized
using T::~T(), then T::T(<b>rhs</b>) is called.</p>
<p><b>Exception Safety:</b> <u>Basic:</u> Exceptions can only be thrown during T::T( T const& );
in that case, <b>*this</b> is left <u>uninitialized</u>.
</p>
<p><b>Throws:</b> Whatever T::operator=( T const& ) or T::T(T const&amp;) throws.</p>
<p><b>Notes:</b> If <b>*this</b> was initialized, T's assignment operator is
used, otherwise, its copy-constructor is used.</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>
<blockquote>
<pre>T x;
optional&lt;T&gt; def ;
optional&lt;T&gt; opt(x) ;
T y;
def = y ;
assert ( *def == y ) ;
opt = y ;
assert ( *opt == y ) ;
// previous value (copy of 'v') destroyed from within 'opt'.
assert ( *opt == y ) ;</pre>
</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 behavior.</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>
</blockquote>
</blockquote>
@ -644,21 +691,25 @@ assert ( *opt == y ) ;
and its value is a <i>copy</i> of the value of <b>rhs</b>; else <b>*this</b>
is uninitialized.
</p>
<p><b>Throws:</b> Whatever T::T( T const& ) throws.</p>
<p><b>Notes:</b> If <b>*this</b> was initialized, it is first reset to uninitialized
using T::~T(), then T::T( T const& ) is called if <b>rhs</b> is initialized.
</p>
<p><b>Exception Safety:</b> <u>Basic:</u> Exceptions can only be thrown during T::T( T const& );
in that case, <b>*this</b> is left <u>uninitialized</u>.
<p><b>Throws:</b> Whatever T::operator( T const&amp;) or&nbsp; T::T( T const& ) throws.</p>
<p><b>Notes:</b> If both<b> *this</b> and <b>rhs</b> are initially initialized,
T's <i>assignment</i> <i>operator</i> is used. If <b>*this</b> is initially initialized but <b>
rhs</b> is uninitialized, T's <i>destructor</i> is called. If <b>*this</b> is initially
uninitialized but rhs is initialized, T's <i>copy constructor</i> is called.
</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>
<blockquote>
<pre>T v;
optional&lt;T&gt; opt(v);
optional&lt;T&gt; uninit ;
optional&lt;T&gt; def ;
opt = uninit ;
assert ( !opt ) ;
opt = def ;
assert ( !def ) ;
// previous value (copy of 'v') destroyed from within 'opt'.
</pre>
@ -667,6 +718,41 @@ assert ( !opt ) ;
<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
behavior.</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>
<blockquote>
<p><b>Effect:</b> Assigns another <i>convertible</i> <b>optional</b> to an <b>optional</b>.</p>
@ -674,14 +760,17 @@ assert ( !opt ) ;
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.
</p>
<p><b>Throws:</b> Whatever T::T( U const& ) throws.</p>
<p><b>Notes:</b> If <b>*this</b> was initialized, it is first reset to uninitialized
using T::~T(), then T::T( U const& ) is called if <b>rhs</b> is initialized,
which requires a valid conversion from U to T.
</p>
<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><b>Throws:</b> Whatever T::operator=( U const&amp; ) or T::T( U const& ) throws.</p>
<p><b>Notes:</b> If both<b> *this</b> and <b>rhs</b> are initially initialized,
T's <i>assignment</i> <i>operator</i> (from U) is used. If <b>*this</b> is initially initialized but <b>
rhs</b> is uninitialized, T's <i>destructor</i> is called. If <b>*this</b> is initially
uninitialized but rhs is initialized, T's <i>converting constructor</i> (from U) is called.
</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>
<blockquote>
<pre>T v;
@ -968,7 +1057,7 @@ assert ( optX != optZ ) ;
<pre>bool operator &gt; ( optional&lt;T&gt; const&amp x, optional&lt;T&gt const&amp y );
</pre>
<blockquote>
<p><b>Returns:</b> !( y &lt; x );</p>
<p><b>Returns:</b> ( y &lt; x );</p>
<p><b>Throws:</b> Nothing.</p>
</blockquote>
@ -1105,7 +1194,7 @@ else print(&quot;employer's name not found!&quot;);
};
</pre>
<h3>Bypassing expensive unnecesary default construction</h3>
<h3>Bypassing expensive unnecessary default construction</h3>
<pre>class ExpensiveCtor { ... } ;
class Fred
{
@ -1118,7 +1207,7 @@ class Fred
<HR>
<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>
<p>However, since references are not real objects some restrictions apply and
@ -1136,11 +1225,90 @@ value, a true real reference is stored so aliasing will ocurr: </p>
<ul>
<li>Copies of optional&lt;T&amp;&gt; will copy the references but all these references
will nonetheless refeer to the same object.</li>
will nonetheless reefer to the same object.</li>
<li>Value-access will actually provide access to the referenced object rather
than the reference itself.</li>
</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 chosen 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 inconsistent behavior w.r.t to the lvalue initialization state.</p>
<p>Imagine optional&lt;T&amp;&gt; forwarding 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>initialized? </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 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.</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>
<H2><A NAME="inplace">In-Place Factories</A></H2>
@ -1170,7 +1338,7 @@ public:
</pre>
<p>A solution to this problem is to support direct construction of the contained
object right in the container's storage.<br>
In this shceme, the user only needs to supply the arguments to the constructor
In this scheme, the user only needs to supply the arguments to the constructor
to use in the wrapped object construction.</p>
<pre>class W
{
@ -1190,12 +1358,12 @@ public:
</pre>
<p>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.</p>
<p>The solution presented in this library is the familiy of <b>InPlaceFactories</b> and
<p>The solution presented in this library is the family of <b>InPlaceFactories</b> and
<b>TypedInPlaceFactories</b>.<br>
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.</p>
<p>&nbsp;For example, one member of this familiy looks like:</p>
<p>&nbsp;For example, one member of this family looks like:</p>
<pre>template&lt;class T,class A0, class A1&gt;
class TypedInPlaceFactory2
{
@ -1268,10 +1436,10 @@ public:
<H2><A NAME="bool">A note about optional&lt;bool&gt;</A></H2>
<p><code>optional&lt;bool&gt;</code> should be used with special caution and consideration.</p>
<p>First, it is functionally similar to a tristate boolean (false,maybe,true) &mdash;such as <u>boost::tribool</u> (not yet formally in boost)&mdash;except that in a tristate boolean,
<p>First, it is functionally similar to a tristate boolean (false,maybe,true) &mdash;such as <a href="http://www.boost.org/doc/html/tribool.html">boost::tribool</a>&mdash;except that in a tristate boolean,
the <i>maybe</i> state <u>represents a valid value</u>, unlike the corresponding state
of an uninitialized optional&lt;bool&gt;.<br>
It should be carefully considered if an optional bool instead of a tribool is really needed</p>
It should be carefully considered if an optional&lt;bool&gt; instead of a tribool is really needed</p>
<p>Second, optional&lt;&gt; provides an implicit conversion to bool. This conversion
refers to the initialization state and not to the contained value.<br>
Using optional&lt;bool&gt; can lead to subtle errors due to the implicit bool conversion:</p>
@ -1432,8 +1600,8 @@ T <u>is not</u> required to be <a href="http://www.sgi.com/tech/stl/DefaultConst
</blockquote>
<HR>
<P>Revised Jannuary 30, 2004</P>
<p><EFBFBD> Copyright Fernando Luis Cacciola Carballal, 2003,2004</p>
<P>Revised April 21, 2005</P>
<p><EFBFBD> Copyright Fernando Luis Cacciola Carballal, 2003,2004,2005</p>
<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_1_0.txt</a> or copy at <a href="http://www.boost.org/LICENSE_1_0.txt">
@ -1443,4 +1611,4 @@ the latest version of this file can be found at <A
HREF="http://www.boost.org">www.boost.org</A>, and the boost
<A HREF="http://www.boost.org/more/mailing_lists.htm#main">discussion lists</A></P>
</BODY>
</HTML>
</HTML>

View File

@ -12,7 +12,7 @@
#ifndef 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
// (bcc<=5.64 cannot include instance data in a precompiled header)
@ -21,7 +21,7 @@ namespace boost {
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,9 +26,11 @@
#include "boost/mpl/bool.hpp"
#include "boost/mpl/not.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/optional/optional_fwd.hpp"
#if BOOST_WORKAROUND(BOOST_MSVC, == 1200)
// VC6.0 has the following bug:
// When a templated assignment operator exist, an implicit conversion
@ -167,7 +169,7 @@ class optional_base : public optional_tag
// Creates an optional<T> uninitialized.
// No-throw
optional_base ( detail::none_t const& )
optional_base ( none_t const& )
:
m_initialized(false) {}
@ -208,32 +210,57 @@ class optional_base : public optional_tag
~optional_base() { destroy() ; }
// 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 )
{
if (is_initialized())
{
if ( rhs.is_initialized() )
assign_value(rhs.get_impl(), is_reference_predicate() );
else destroy();
}
else
{
destroy();
if ( rhs.is_initialized() )
construct(rhs.get_impl());
}
}
// Assigns from another _convertible_ optional<U> (deep-copies the rhs value)
template<class U>
void assign ( optional<U> const& rhs )
{
if (is_initialized())
{
if ( rhs.is_initialized() )
assign_value(static_cast<value_type>(rhs.get()), is_reference_predicate() );
else destroy();
}
else
{
if ( rhs.is_initialized() )
construct(static_cast<value_type>(rhs.get()));
}
}
// 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 )
{
destroy();
construct(val);
}
{
if (is_initialized())
assign_value(val, is_reference_predicate() );
else construct(val);
}
// Assigns from "none", destroying the current value, if any, leaving this UNINITIALIZED
// 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
template<class Expr>
void assign_expr ( Expr const& expr, Expr const* tag )
{
destroy();
construct(expr,tag);
if (is_initialized())
assign_expr_to_initialized(expr,tag);
else construct(expr,tag);
}
#endif
@ -244,7 +271,6 @@ class optional_base : public optional_tag
void reset() { destroy(); }
// 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); }
// Returns a pointer to the value if this is initialized, otherwise,
@ -281,6 +307,21 @@ class optional_base : public optional_tag
factory.apply(m_storage.address()) ;
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
// Constructs using any expression implicitely convertible to the single argument
@ -294,6 +335,16 @@ class optional_base : public optional_tag
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
// BCB5.64 (and probably lower versions) workaround.
// The in-place factories are supported by means of catch-all constructors
@ -321,11 +372,14 @@ class optional_base : public optional_tag
}
#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()
{
if ( m_initialized )
destroy_impl(is_reference_predicate()) ;
}
{
if ( m_initialized )
destroy_impl(is_reference_predicate()) ;
}
unspecified_bool_type safe_bool() const { return m_initialized ? &this_type::is_initialized : 0 ; }
@ -391,7 +445,7 @@ class optional : public optional_detail::optional_base<T>
// Creates an optional<T> uninitialized.
// No-throw
optional( detail::none_t const& none_ ) : base(none_) {}
optional( none_t const& none_ ) : base(none_) {}
// Creates an optional<T> initialized with 'val'.
// Can throw if T::T(T const&) does
@ -446,6 +500,7 @@ class optional : public optional_detail::optional_base<T>
}
#endif
#ifndef BOOST_OPTIONAL_NO_CONVERTING_ASSIGNMENT
// Assigns from another convertible optional<U> (converts && deep-copies the rhs value)
// Requires a valid conversion from U to T.
@ -453,14 +508,7 @@ class optional : public optional_detail::optional_base<T>
template<class U>
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);
return *this ;
}
#endif
@ -485,7 +533,7 @@ class optional : public optional_detail::optional_base<T>
// Assigns from a "none"
// Which destroys the current value, if any, leaving this UNINITIALIZED
// No-throw (assuming T::~T() doesn't)
optional& operator= ( detail::none_t const& none_ )
optional& operator= ( none_t const& none_ )
{
this->assign( none_ ) ;
return *this ;
@ -607,62 +655,62 @@ bool operator >= ( optional<T> const& x, optional<T> const& y )
template<class T>
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>() ); }
template<class T>
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>() ); }
template<class T>
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 ) ; }
template<class T>
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 ; }
template<class T>
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 ) ; }
template<class T>
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 ) ; }
template<class T>
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); }
template<class T>
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); }
template<class T>
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 ) ; }
template<class T>
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 ; }
template<class T>
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 ) ; }
template<class T>
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 ) ; }
//
@ -679,8 +727,9 @@ namespace optional_detail {
#endif
// optional's swap:
// If both are initialized, calls swap(T&, T&), with whatever exception guarantess are given there.
// If only one is initialized, calls I.reset() and U.reset(*I), with the Basic Guarantee
// 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 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)
template<class T>
inline
@ -688,12 +737,12 @@ void optional_swap ( optional<T>& x, optional<T>& y )
{
if ( !x && !!y )
{
x.reset(*y); // Basic guarantee.
x.reset(*y);
y.reset();
}
else if ( !!x && !y )
{
y.reset(*x); // Basic guarantee.
y.reset(*x);
x.reset();
}
else if ( !!x && !!y )
@ -714,6 +763,11 @@ template<class T> inline void swap ( optional<T>& x, optional<T>& y )
optional_detail::optional_swap(x,y);
}
template<class T> inline optional<T> make_optional ( T const& v )
{
return optional<T>(v);
}
} // namespace boost
#endif

View File

@ -70,7 +70,7 @@ void test_basics( T const* )
// Implicit construction
// The first parameter is implicitely converted to optional<T>(a);
test_implicit_construction(a,a,z);
// Direct initialization.
// 'oa' state is Initialized with 'a'
// T::T( T const& x ) is used.
@ -85,7 +85,7 @@ void test_basics( T const* )
optional<T> ob ;
// Value-Assignment upon Uninitialized optional.
// T::T ( T const& x ) is used.
// T::T( T const& x ) is used.
set_pending_copy( ARG(T) ) ;
ob = a ;
check_is_not_pending_copy( ARG(T) ) ;
@ -93,12 +93,14 @@ void test_basics( T const* )
check_value(ob,a,z);
// Value-Assignment upon Initialized optional.
// T::T ( T const& x ) is used
set_pending_dtor( ARG(T) ) ;
set_pending_copy( ARG(T) ) ;
// T::operator=( T const& x ) is used
set_pending_assign( ARG(T) ) ;
set_pending_copy ( ARG(T) ) ;
set_pending_dtor ( ARG(T) ) ;
ob = b ;
check_is_not_pending_dtor( ARG(T) ) ;
check_is_not_pending_copy( ARG(T) ) ;
check_is_not_pending_assign( ARG(T) ) ;
check_is_pending_copy ( ARG(T) ) ;
check_is_pending_dtor ( ARG(T) ) ;
check_initialized(ob);
check_value(ob,b,z);
@ -111,13 +113,14 @@ void test_basics( T const* )
check_value_const(oa2,a,z);
// Assignment
// T::~T() is used to destroy previous value in ob.
// T::T ( T const& x ) is used to copy new value.
set_pending_dtor( ARG(T) ) ;
set_pending_copy( ARG(T) ) ;
// T::operator= ( T const& x ) is used to copy new value.
set_pending_assign( ARG(T) ) ;
set_pending_copy ( ARG(T) ) ;
set_pending_dtor ( ARG(T) ) ;
oa = ob ;
check_is_not_pending_dtor( ARG(T) ) ;
check_is_not_pending_copy( ARG(T) ) ;
check_is_not_pending_assign( ARG(T) ) ;
check_is_pending_copy ( ARG(T) ) ;
check_is_pending_dtor ( ARG(T) ) ;
check_initialized(oa);
check_value(oa,b,z);
@ -161,7 +164,7 @@ template<class T>
void test_direct_value_manip( T const* )
{
TRACE( std::endl << BOOST_CURRENT_FUNCTION );
T x(3);
optional<T> const c_opt0(x) ;
@ -169,7 +172,7 @@ void test_direct_value_manip( T const* )
BOOST_CHECK( c_opt0.get().V() == x.V() ) ;
BOOST_CHECK( opt0.get().V() == x.V() ) ;
BOOST_CHECK( c_opt0->V() == x.V() ) ;
BOOST_CHECK( opt0->V() == x.V() ) ;
@ -212,7 +215,7 @@ void test_uninitialized_access( T const* )
}
catch (...) {}
BOOST_CHECK(!passed);
passed = false ;
try
{
@ -282,6 +285,9 @@ void test_throwing_direct_init( T const* )
BOOST_CHECK(!passed);
check_is_not_pending_copy( 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_instance_count(count, ARG(T) );
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 a(8);
T b(9);
T x(-1);
int count = get_instance_count( ARG(T) ) ;
reset_throw_on_copy( ARG(T) ) ;
optional<T> opt ( b ) ;
++ count ;
@ -342,16 +349,16 @@ void test_throwing_val_assign_on_initialized( T const* )
check_value(opt,b,z);
set_throw_on_copy( ARG(T) ) ;
set_throw_on_assign( ARG(T) ) ;
bool passed = false ;
try
{
// This should:
// Attempt to copy construct 'a' and throw.
// opt should be left uninitialized (even though it was initialized)
set_pending_dtor( ARG(T) ) ;
set_pending_copy( ARG(T) ) ;
// Attempt to assign 'a' and throw.
// opt is kept initialized but its value not neccesarily fully assigned
// (in this test, incompletely assigned is flaged with the value -1 being set)
set_pending_assign( ARG(T) ) ;
opt.reset ( a ) ;
passed = true ;
}
@ -359,12 +366,12 @@ void test_throwing_val_assign_on_initialized( T const* )
BOOST_CHECK(!passed);
-- count ;
check_is_not_pending_dtor( ARG(T) );
check_is_not_pending_copy( ARG(T) );
check_is_not_pending_assign( 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 a(10);
reset_throw_on_copy( ARG(T) ) ;
optional<T> opt (a);
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.
check_initialized(opt);
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 a(11);
reset_throw_on_copy( ARG(T) ) ;
optional<T> opt0 ;
optional<T> opt1(a) ;
@ -446,6 +451,8 @@ void test_throwing_assign_to_uninitialized( T const* )
check_is_not_pending_copy( ARG(T) );
check_instance_count(count, ARG(T) );
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 a(12);
T b(13);
reset_throw_on_copy( ARG(T) ) ;
T x(-1);
optional<T> opt0(a) ;
optional<T> opt1(b) ;
int count = get_instance_count( ARG(T) ) ;
set_throw_on_copy( ARG(T) ) ;
set_throw_on_assign( ARG(T) ) ;
bool passed = false ;
try
{
// This should:
// Attempt to copy construct 'opt1.value()' into opt0 and throw.
// opt0 should be left unmodified or uninitialized
set_pending_dtor( ARG(T) ) ;
set_pending_copy( ARG(T) ) ;
// opt0 is kept initialized but its value not neccesarily fully assigned
// (in this test, incompletely assigned is flaged with the value -1 being set)
set_pending_assign( ARG(T) ) ;
opt0 = opt1 ;
passed = true ;
}
@ -486,11 +492,12 @@ void test_throwing_assign_to_initialized( T const* )
BOOST_CHECK(!passed);
// opt0 was left uninitialized
-- count ;
check_is_not_pending_dtor( ARG(T) );
check_is_not_pending_copy( ARG(T) );
check_is_not_pending_assign( 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) ) ;
}
//
@ -500,13 +507,11 @@ template<class T>
void test_no_throwing_swap( T const* )
{
TRACE( std::endl << BOOST_CURRENT_FUNCTION );
T z(0);
T a(14);
T b(15);
reset_throw_on_copy( ARG(T) ) ;
optional<T> def0 ;
optional<T> def1 ;
optional<T> opt0(a) ;
@ -541,16 +546,15 @@ template<class T>
void test_throwing_swap( T const* )
{
TRACE( std::endl << BOOST_CURRENT_FUNCTION );
T a(16);
T b(17);
reset_throw_on_copy( ARG(T) ) ;
T x(-1);
optional<T> opt0(a) ;
optional<T> opt1(b) ;
set_throw_on_copy( ARG(T) ) ;
set_throw_on_assign( ARG(T) ) ;
//
// Case 1: Both Initialized.
@ -567,14 +571,18 @@ void test_throwing_swap( T const* )
BOOST_CHECK(!passed);
// Assuming swap(T&,T&) has at least the basic guarantee, these should hold.
BOOST_CHECK( ( !opt0 || ( !!opt0 && ( ( *opt0 == a ) || ( *opt0 == b ) ) ) ) ) ;
BOOST_CHECK( ( !opt1 || ( !!opt1 && ( ( *opt1 == a ) || ( *opt1 == b ) ) ) ) ) ;
// optional's swap doesn't affect the initialized states of the arguments. Therefore,
// the following must hold:
check_initialized(opt0);
check_initialized(opt1);
check_value(opt0,x,a);
check_value(opt1,b,x);
//
// Case 2: Only one Initialized.
//
reset_throw_on_copy( ARG(T) ) ;
reset_throw_on_assign( ARG(T) ) ;
opt0.reset();
opt1.reset(a);
@ -585,7 +593,7 @@ void test_throwing_swap( T const* )
try
{
// 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);
passed = true ;
@ -596,7 +604,9 @@ void test_throwing_swap( T const* )
check_uninitialized(opt0);
check_initialized(opt1);
check_value(opt1,a,b);
check_value(opt1,a,x);
reset_throw_on_copy( ARG(T) ) ;
}
//
@ -606,9 +616,7 @@ template<class T>
void test_relops( T const* )
{
TRACE( std::endl << BOOST_CURRENT_FUNCTION );
reset_throw_on_copy( ARG(T) ) ;
T v0(18);
T v1(19);
T v2(19);
@ -627,11 +635,11 @@ void test_relops( T const* )
// Check when both are uininitalized.
BOOST_CHECK ( def0 == def1 ) ; // both uninitialized compare equal
BOOST_CHECK ( !(def0 < def1) ) ; // uninitialized is never less than uninitialized
BOOST_CHECK ( !(def0 < def1) ) ; // uninitialized is never less than uninitialized
BOOST_CHECK ( !(def0 > def1) ) ; // uninitialized is never greater than uninitialized
BOOST_CHECK ( !(def0 != def1) ) ;
BOOST_CHECK ( def0 <= def1 ) ;
BOOST_CHECK ( def0 >= def1 ) ;
BOOST_CHECK ( def0 <= def1 ) ;
BOOST_CHECK ( def0 >= def1 ) ;
// Check when only lhs is uninitialized.
BOOST_CHECK ( def0 != opt0 ) ; // uninitialized is never equal to initialized
@ -664,7 +672,7 @@ void test_none( T const* )
TRACE( std::endl << BOOST_CURRENT_FUNCTION );
using boost::none ;
optional<T> def0 ;
optional<T> def1(none) ;
optional<T> non_def( T(1234) ) ;
@ -682,7 +690,7 @@ void test_none( T const* )
void test_with_builtin_types()
{
TRACE( std::endl << BOOST_CURRENT_FUNCTION );
test_basics( ARG(double) );
test_uninitialized_access( ARG(double) );
test_no_throwing_swap( ARG(double) );
@ -763,7 +771,7 @@ void test_conversions1()
optional<double> opt3 ;
opt3 = opt2 ;
BOOST_CHECK(*opt3 == d);
#endif
#endif
}
void test_conversions2()

View File

@ -93,10 +93,22 @@ class X
X& operator= ( X const& rhs )
{
v = rhs.v ;
pending_assign = false ;
TRACE ( "X::operator =( X const& rhs). this=" << this << " rhs.v=" << rhs.v ) ;
if ( throw_on_assign )
{
TRACE ( "throwing exception in X's assignment" ) ;
v = -1 ;
throw 0 ;
}
else
{
v = rhs.v ;
TRACE ( "X::operator =( X const& rhs). this=" << this << " rhs.v=" << rhs.v ) ;
}
return *this ;
}
@ -115,7 +127,9 @@ class X
static int count ;
static bool pending_copy ;
static bool pending_dtor ;
static bool pending_assign ;
static bool throw_on_copy ;
static bool throw_on_assign ;
private :
@ -127,32 +141,44 @@ class X
} ;
int X::count = 0 ;
bool X::pending_copy = false ;
bool X::pending_dtor = false ;
bool X::throw_on_copy = false ;
int X::count = 0 ;
bool X::pending_copy = false ;
bool X::pending_dtor = false ;
bool X::pending_assign = 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_dtor ( X const* x ) { X::pending_dtor = true ; }
inline void set_throw_on_copy ( X const* x ) { X::throw_on_copy = true ; }
inline void reset_throw_on_copy ( X const* x ) { X::throw_on_copy = false ; }
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_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_instance_count ( int c, X const* x ) { BOOST_CHECK( X::count == c ) ; }
inline int get_instance_count ( X const* x ) { return X::count ; }
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_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_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_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_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_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 int get_instance_count ( X const* x ) { return X::count ; }
inline void set_pending_copy (...) {}
inline void set_pending_dtor (...) {}
inline void set_throw_on_copy (...) {}
inline void reset_throw_on_copy (...) {}
inline void check_is_pending_copy (...) {}
inline void check_is_pending_dtor (...) {}
inline void check_is_not_pending_copy(...) {}
inline void check_is_not_pending_dtor(...) {}
inline void check_instance_count (...) {}
inline int get_instance_count (...) { return 0 ; }
inline void set_pending_copy (...) {}
inline void set_pending_dtor (...) {}
inline void set_pending_assign (...) {}
inline void set_throw_on_copy (...) {}
inline void set_throw_on_assign (...) {}
inline void reset_throw_on_copy (...) {}
inline void reset_throw_on_assign (...) {}
inline void check_is_pending_copy (...) {}
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_dtor (...) {}
inline void check_is_not_pending_assign(...) {}
inline void check_instance_count (...) {}
inline int get_instance_count (...) { return 0 ; }
template<class T>
@ -160,7 +186,7 @@ inline void check_uninitialized_const ( optional<T> const& opt )
{
#ifndef BOOST_OPTIONAL_NO_NULL_COMPARE
BOOST_CHECK( opt == 0 ) ;
#endif
#endif
BOOST_CHECK( !opt ) ;
BOOST_CHECK( !get_pointer(opt) ) ;
BOOST_CHECK( !opt.get_ptr() ) ;

View File

@ -32,7 +32,7 @@ inline void check_ref_uninitialized_const ( optional<T&> const& opt )
{
#ifndef BOOST_OPTIONAL_NO_NULL_COMPARE
BOOST_CHECK( opt == 0 ) ;
#endif
#endif
BOOST_CHECK( !opt ) ;
}
template<class T>
@ -104,9 +104,16 @@ void test_basics( T const* )
T z(0);
T original_a(1);
T a(1);
T b(2);
T c(10);
T& aref = a ;
T& bref = b ;
// Default construction.
// 'def' state is Uninitialized.
@ -115,13 +122,16 @@ void test_basics( T const* )
check_ref_uninitialized(def);
// 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.
set_pending_copy( ARG(T) ) ;
optional<T&> oa ( aref ) ;
check_is_pending_copy( ARG(T) );
check_ref_initialized(oa);
check_ref_value(oa,a,z);
*oa = b ; // changes the value of 'a' through the reference
BOOST_CHECK( a == b ) ;
// Copy initialization.
// 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_ref_initialized_const(oa2);
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 ;
// Value-Assignment upon Uninitialized optional.
// T::T ( T const& x ) is NOT used becasue the optional holds a reference.
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_ref_initialized(ob);
check_ref_value(ob,a,z);
a = c;
check_ref_value(ob,a,z);
// Value-Assignment upon Initialized optional.
// T::T ( T const& x ) is NOT used becasue the optional holds a reference.
set_pending_dtor( ARG(T) ) ;
set_pending_copy( ARG(T) ) ;
ob = b ;
check_is_pending_dtor( ARG(T) ) ;
check_is_pending_copy( ARG(T) ) ;
// T::operator= ( T const& x ) is used.
set_pending_assign( ARG(T) ) ;
ob = b ; // Rebinds 'ob' to 'b' (without changing 'a')
check_is_pending_assign( ARG(T) ) ;
check_ref_initialized(ob);
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.
// T::T ( T const& x ) is NOT used becasue the optional holds a reference.
@ -162,14 +177,12 @@ void test_basics( T const* )
// Assignment
// T::~T() is used to destroy previous value in ob.
// T::T ( T const& x ) is NOT used becasue the optional holds a reference.
set_pending_dtor( ARG(T) ) ;
set_pending_copy( ARG(T) ) ;
oa = ob ;
check_is_pending_dtor( ARG(T) ) ;
check_is_pending_copy( ARG(T) ) ;
// T::operator=( T const& x ) is used.
set_pending_assign( ARG(T) ) ;
oa = ob ; // Rebinds 'a' to 'b'
check_is_pending_assign( ARG(T) ) ;
check_ref_initialized(oa);
a = original_a ;
check_ref_value(oa,b,z);
// Uninitializing Assignment upon Initialized Optional
@ -190,6 +203,7 @@ void test_basics( T const* )
check_is_pending_copy( ARG(T) ) ;
check_ref_uninitialized(oa);
// Deinitialization of Initialized Optional
// T::~T() is NOT used becasue the optional holds a reference.
set_pending_dtor( ARG(T) ) ;
@ -212,9 +226,9 @@ template<class T>
void test_relops( T const* )
{
TRACE( std::endl << BOOST_CURRENT_FUNCTION );
reset_throw_on_copy( ARG(T) ) ;
T v0(18);
T v1(19);
T v2(19);
@ -233,11 +247,11 @@ void test_relops( T const* )
// Check when both are uininitalized.
BOOST_CHECK ( def0 == def1 ) ; // both uninitialized compare equal
BOOST_CHECK ( !(def0 < def1) ) ; // uninitialized is never less than uninitialized
BOOST_CHECK ( !(def0 > def1) ) ; // uninitialized is never greater than uninitialized
BOOST_CHECK ( !(def0 < def1) ) ; // uninitialized is never less than uninitialized
BOOST_CHECK ( !(def0 > def1) ) ; // uninitialized is never greater than uninitialized
BOOST_CHECK ( !(def0 != def1) ) ;
BOOST_CHECK ( def0 <= def1 ) ;
BOOST_CHECK ( def0 >= def1 ) ;
BOOST_CHECK ( def0 <= def1 ) ;
BOOST_CHECK ( def0 >= def1 ) ;
// Check when only lhs is uninitialized.
BOOST_CHECK ( def0 != opt0 ) ; // uninitialized is never equal to initialized
@ -254,7 +268,7 @@ void test_relops( T const* )
BOOST_CHECK ( opt0 > def0 ) ;
BOOST_CHECK ( !(opt0 <= def0) ) ;
BOOST_CHECK ( opt0 >= opt0 ) ;
// If both are initialized, values are compared
BOOST_CHECK ( opt0 != opt1 ) ;
BOOST_CHECK ( opt1 == opt2 ) ;
@ -270,7 +284,7 @@ void test_none( T const* )
TRACE( std::endl << BOOST_CURRENT_FUNCTION );
using boost::none ;
T a(1234);
optional<T&> def0 ;