utility/value_init.htm
Beman Dawes b3a971e7e9 Copyright and/or License cleanup
[SVN r40890]
2007-11-07 16:08:09 +00:00

219 lines
12 KiB
HTML
Raw Blame History

<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=iso-8859-1">
<title>value_initialized</title>
</head>
<body vlink="#800080" link="#0000ff" text="#000000" bgcolor="#ffffff">
<h2><img src="../../boost.png" width="276" height="86">
Header &lt;<a href="../../boost/utility/value_init.hpp">boost/utility/value_init.hpp</a>&gt;
</h2>
<h2>Contents</h2>
<dl>
<dt><a href="#rationale">Rationale</a></dt>
<dt><a href="#intro">Introduction</a></dt>
</dl>
<ul>
<li><a href="#valueinit">value-initialization</a></li>
<li><a href="#valueinitsyn">value-initialization syntax</a></li>
</ul>
<dl class="page-index">
<dt><a href="#types">Types</a></dt>
</dl>
<ul>
<li><a href="#val_init"><code>value_initialized&lt;&gt;</code></a></li>
</ul>
<a href="#acknowledgements">Acknowledgements</a><br>
<br>
<hr>
<h2><a name="rationale"></a>Rationale</h2>
<p>Constructing and initializing objects in a generic way is difficult in
C++. The problem is that there are several different rules that apply
for initialization. Depending on the type, the value of a newly constructed
object can be zero-initialized (logically 0), default-constructed (using
the default constructor), or indeterminate. When writing generic code,
this problem must be addressed. <code>value_initialized</code> provides
a solution with consistent syntax for value initialization of scalar,
union and class types. <br>
</p>
<h2><a name="intro"></a>Introduction</h2>
<p>The C++ standard [<a href="#references">1</a>] contains the definitions
of <code>zero-initialization</code> and <code>default-initialization</code>.
Informally, zero-initialization means that the object is given the initial
value 0 (converted to the type) and default-initialization means that
POD [<a href="#references">2</a>] types are zero-initialized, while class
types are initialized with their corresponding default constructors. A
<i>declaration</i> can contain an <i>initializer</i>, which specifies the
object's initial value. The initializer can be just '()', which states that
the object shall be default-initialized (but see below). However, if a <i>declaration</i>
has no <i>initializer</i> and it is of a non-<code>const</code>, non-<code>static</code>
POD type, the initial value is indeterminate:<cite>(see &sect;8.5 for the
accurate definitions).</cite></p>
<pre>int x ; // no initializer. x value is indeterminate.<br>std::string s ; // no initializer, s is default-constructed.<br><br>int y = int() ; <br>// y is initialized using copy-initialization<br>// but the temporary uses an empty set of parentheses as the initializer,<br>// so it is default-constructed.<br>// A default constructed POD type is zero-initialized,<br>// therefore, y == 0.<br><br>void foo ( std::string ) ;<br>foo ( std::string() ) ; <br>// the temporary string is default constructed <br>// as indicated by the initializer () </pre>
<h3><a name="valueinit">value-initialization</a></h3>
<p>The first <a
href="http://anubis.dkuug.dk/JTC1/SC22/WG21/docs/cwg_defects.html">Technical
Corrigendum for the C++ Standard</a> (TC1), whose draft was released to
the public in November 2001, introduced <a
href="http://anubis.dkuug.dk/JTC1/SC22/WG21/docs/cwg_defects.html#178">Core
Issue 178</a> (among many other issues, of course).</p>
<p> That issue introduced the new concept of <code>value-initialization</code>
(it also fixed the wording for zero-initialization). Informally, value-initialization
is similar to default-initialization with the exception that in some cases
non-static data members and base class sub-objects are also value-initialized.
The difference is that an object that is value-initialized won't have
(or at least is less likely to have) indeterminate values for data members
and base class sub-objects; unlike the case of an object default constructed.
(see Core Issue 178 for a normative description).</p>
<p>In order to specify value-initialization of an object we need to use the
empty-set initializer: (). </p>
<p><i>(but recall that the current C++ Standard states that '()' invokes default-initialization,
not value-initialization)</i></p>
<p>As before, a declaration with no intializer specifies default-initialization,
and a declaration with a non-empty initializer specifies copy (=xxx) or
direct (xxx) initialization. </p>
<pre>template&lt;class T&gt; void eat(T);<br>int x ; // indeterminate initial value.<br>std::string s; // default-initialized.<br>eat ( int() ) ; // value-initialized<br>eat ( std::string() ) ; // value-initialied</pre>
<h4><a name="valueinitsyn">value-initialization</a> syntax</h4>
<p>Value initialization is specified using (). However, the empty set of
parentheses is not permitted by the syntax of initializers because it is
parsed as the declaration of a function taking no arguments: </p>
<pre>int x() ; // declares function int(*)()<br>int y ( int() ) ; // decalares function int(*)( int(*)() )</pre>
<p>Thus, the empty () must be put in some other initialization context.</p>
<p>One alternative is to use copy-initialization syntax:</p>
<pre>int x = int() ;</pre>
<p>This works perfectly fine for POD types. But for non-POD class types,
copy-initialization searches for a suitable constructor, which could be,
for instance, the copy-constructor (it also searches for a suitable conversion
sequence but this doesn't apply in this context). For an arbitrary unknown
type, using this syntax may not have the value-initialization effect intended
because we don't know if a copy from a default constructed object is exactly
the same as a default constructed object, and the compiler is allowed (in
some cases), but never required to, optimize the copy away.</p>
<p>One possible generic solution is to use value-initialization of a non static
data member:</p>
<pre>template&lt;class T&gt; <br>struct W <br>{<br> // value-initialization of 'data' here.<br> W() : data() {}<br> T data ;<br>} ;<br>W&lt;int&gt; w ;<br>// w.data is value-initialized for any type. </pre>
<p><code>This is the solution supplied by the value_initialized&lt;&gt; template
class.</code></p>
<h2><a name="types"></a>Types</h2>
<h2><a name="val_init"><code>template class value_initialized&lt;T&gt;</code></a></h2>
<pre>namespace boost {<br><br>template&lt;class T&gt;<br>class value_initialized<br>{<br> public :<br> value_initialized() : x() {}<br> operator T&amp;() const { return x ; }<br> T&amp; data() const { return x ; }<br><br> private :<br> <i>unspecified</i> x ;<br>} ;<br><br>template&lt;class T&gt;<br>T const&amp; get ( value_initialized&lt;T&gt; const&amp; x )<br>{<br> return x.data() ;<br>}<br><br>template&lt;class T&gt;<br>T&amp; get ( value_initialized&lt;T&gt;&amp; x )<br>{<br> return x.data() ;<br>}<br><br>} // namespace boost<br></pre>
<p>An object of this template class is a <code>T</code>-wrapper convertible
to <code>'T&amp;'</code> whose wrapped object (data member of type <code>T</code>)
is <a href="#valueinit">value-initialized</a> upon default-initialization
of this wrapper class: </p>
<pre>int zero = 0 ;<br>value_initialized&lt;int&gt; x ;<br>assert ( x == zero ) ;<br><br>std::string def ;<br>value_initialized&lt; std::string &gt; y ;<br>assert ( y == def ) ;<br></pre>
<p>The purpose of this wrapper is to provide a consistent syntax for value
initialization of scalar, union and class types (POD and non-POD) since
the correct syntax for value initialization varies (see <a
href="#valueinitsyn">value-initialization syntax</a>)</p>
<p>The wrapped object can be accessed either through the conversion operator
<code>T&amp;</code>, the member function <code>data()</code>, or the
non-member function <code>get()</code>: </p>
<pre>void watch(int);<br>value_initialized&lt;int&gt; x;<br><br>watch(x) ; // operator T&amp; used.<br>watch(x.data());<br>watch( get(x) ) // function get() used</pre>
<p>Both <code>const</code> and non-<code>const</code> objects can be wrapped.
Mutable objects can be modified directly from within the wrapper but constant
objects cannot:</p>
<pre>value_initialized&lt;int&gt; x ; <br>static_cast&lt;int&amp;&gt;(x) = 1 ; // OK<br>get(x) = 1 ; // OK<br><br>value_initialized&lt;int const&gt; y ; <br>static_cast&lt;int&amp;&gt;(y) = 1 ; // ERROR: cannot cast to int&amp;<br>static_cast&lt;int const&amp;&gt;(y) = 1 ; // ERROR: cannot modify a const value<br>get(y) = 1 ; // ERROR: cannot modify a const value</pre>
<h3>Warning:</h3>
<p>Both the conversion operator and the <code>data()</code> member function
are <code>const</code> in order to allow access to the wrapped object
from a constant wrapper:</p>
<pre>void foo(int);<br>value_initialized&lt;int&gt; const x ;<br>foo(x);<br></pre>
<p>But notice that this conversion operator is to <code>T&amp;</code> although
it is itself <code>const</code>. As a consequence, if <code>T</code> is
a non-<code>const</code> type, you can modify the wrapped object even from
within a constant wrapper:</p>
<pre>value_initialized&lt;int&gt; const x_c ;<br>int&amp; xr = x_c ; // OK, conversion to int&amp; available even though x_c is itself const.<br>xr = 2 ; </pre>
<p>The reason for this obscure behavior is that some commonly used compilers
just don't accept the following valid code:</p>
<pre>struct X<br>{<br> operator int&amp;() ;<br> operator int const&amp;() const ; <br>};<br>X x ;<br>(x == 1 ) ; // ERROR HERE!</pre>
<p>These compilers complain about ambiguity between the conversion operators.
This complaint is incorrect, but the only workaround that I know of is
to provide only one of them, which leads to the obscure behavior just explained.<br>
</p>
<h3>Recommended practice: The non-member get() idiom</h3>
<p>The obscure behavior of being able to modify a non-<code>const</code>
wrapped object from within a constant wrapper can be avoided if access to
the wrapped object is always performed with the <code>get()</code> idiom:</p>
<pre>value_initialized&lt;int&gt; x ;<br>get(x) = 1 ; // OK<br><br>value_initialized&lt;int const&gt; cx ;<br>get(x) = 1 ; // ERROR: Cannot modify a const object<br><br>value_initialized&lt;int&gt; const x_c ;<br>get(x_c) = 1 ; // ERROR: Cannot modify a const object<br><br>value_initialized&lt;int const&gt; const cx_c ;<br>get(cx_c) = 1 ; // ERROR: Cannot modify a const object<br></pre>
<h3><a name="references">References</a></h3>
[1] The C++ Standard, ISO/IEC 14882:98 <br>
[2] Plain Old Data
<h3><a name="acknowledgements"></a>Acknowledgements</h3>
value_initialized was developed by Fernando Cacciola, with help and
suggestions from David Abrahams and Darin Adler.<br>
Special thanks to Bj<42>rn Karlsson who carefully edited and completed this documentation.
<p>Developed by <a href="mailto:fernando_cacciola@hotmail.com">Fernando Cacciola</a>,
the latest version of this file can be found at <a
href="http://www.boost.org">www.boost.org</a>, and the boost discussion list
at <a href="http://www.yahoogroups.com/list/boost">www.yahoogroups.com/list/boost</a>.
</p>
<hr>
<p>Revised 19 September 2002</p>
<p>&copy; Copyright Fernando Cacciola, 2002.</p>
<p>Distributed under the Boost Software License, Version 1.0. See
<a href="http://www.boost.org/LICENSE_1_0.txt">www.boost.org/LICENSE_1_0.txt</a></p>
<br>
<br>
</body>
</html>