basic.xml 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
  3. "http://www.boost.org/tools/boostbook/dtd/boostbook.dtd">
  4. <!--
  5. Copyright 2003, Eric Friedman, Itay Maman.
  6. Distributed under the Boost Software License, Version 1.0. (See accompanying
  7. file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  8. -->
  9. <section id="variant.tutorial.basic">
  10. <title>Basic Usage</title>
  11. <using-namespace name="boost"/>
  12. <using-class name="boost::variant"/>
  13. <para>A discriminated union container on some set of types is defined by
  14. instantiating the <code><classname>boost::variant</classname></code> class
  15. template with the desired types. These types are called
  16. <emphasis role="bold">bounded types</emphasis> and are subject to the
  17. requirements of the
  18. <link linkend="variant.concepts.bounded-type"><emphasis>BoundedType</emphasis></link>
  19. concept. Any number of bounded types may be specified, up to some
  20. implementation-defined limit (see
  21. <code><macroname>BOOST_VARIANT_LIMIT_TYPES</macroname></code>).</para>
  22. <para>For example, the following declares a discriminated union container on
  23. <code>int</code> and <code>std::string</code>:
  24. <programlisting><classname>boost::variant</classname>&lt; int, std::string &gt; v;</programlisting>
  25. </para>
  26. <para>By default, a <code>variant</code> default-constructs its first
  27. bounded type, so <code>v</code> initially contains <code>int(0)</code>. If
  28. this is not desired, or if the first bounded type is not
  29. default-constructible, a <code>variant</code> can be constructed
  30. directly from any value convertible to one of its bounded types. Similarly,
  31. a <code>variant</code> can be assigned any value convertible to one of its
  32. bounded types, as demonstrated in the following:
  33. <programlisting>v = "hello";</programlisting>
  34. </para>
  35. <para>Now <code>v</code> contains a <code>std::string</code> equal to
  36. <code>"hello"</code>. We can demonstrate this by
  37. <emphasis role="bold">streaming</emphasis>&nbsp;<code>v</code> to standard
  38. output:
  39. <programlisting>std::cout &lt;&lt; v &lt;&lt; std::endl;</programlisting>
  40. </para>
  41. <para>Usually though, we would like to do more with the content of a
  42. <code>variant</code> than streaming. Thus, we need some way to access the
  43. contained value. There are two ways to accomplish this:
  44. <code><functionname>apply_visitor</functionname></code>, which is safest
  45. and very powerful, and
  46. <code><functionname>get</functionname>&lt;T&gt;</code>, which is
  47. sometimes more convenient to use.</para>
  48. <para>For instance, suppose we wanted to concatenate to the string contained
  49. in <code>v</code>. With <emphasis role="bold">value retrieval</emphasis>
  50. by <code><functionname>get</functionname></code>, this may be accomplished
  51. quite simply, as seen in the following:
  52. <programlisting>std::string&amp; str = <functionname>boost::get</functionname>&lt;std::string&gt;(v);
  53. str += " world! ";</programlisting>
  54. </para>
  55. <para>As desired, the <code>std::string</code> contained by <code>v</code> now
  56. is equal to <code>"hello world! "</code>. Again, we can demonstrate this by
  57. streaming <code>v</code> to standard output:
  58. <programlisting>std::cout &lt;&lt; v &lt;&lt; std::endl;</programlisting>
  59. </para>
  60. <para>While use of <code>get</code> is perfectly acceptable in this trivial
  61. example, <code>get</code> generally suffers from several significant
  62. shortcomings. For instance, if we were to write a function accepting a
  63. <code>variant&lt;int, std::string&gt;</code>, we would not know whether
  64. the passed <code>variant</code> contained an <code>int</code> or a
  65. <code>std::string</code>. If we insisted upon continued use of
  66. <code>get</code>, we would need to query the <code>variant</code> for its
  67. contained type. The following function, which &quot;doubles&quot; the
  68. content of the given <code>variant</code>, demonstrates this approach:
  69. <programlisting>void times_two( boost::variant&lt; int, std::string &gt; &amp; operand )
  70. {
  71. if ( int* pi = <functionname>boost::get</functionname>&lt;int&gt;( &amp;operand ) )
  72. *pi *= 2;
  73. else if ( std::string* pstr = <functionname>boost::get</functionname>&lt;std::string&gt;( &amp;operand ) )
  74. *pstr += *pstr;
  75. }</programlisting>
  76. </para>
  77. <para>However, such code is quite brittle, and without careful attention will
  78. likely lead to the introduction of subtle logical errors detectable only at
  79. runtime. For instance, consider if we wished to extend
  80. <code>times_two</code> to operate on a <code>variant</code> with additional
  81. bounded types. Specifically, let's add
  82. <code>std::complex&lt;double&gt;</code> to the set. Clearly, we would need
  83. to at least change the function declaration:
  84. <programlisting>void times_two( boost::variant&lt; int, std::string, std::complex&lt;double&gt; &gt; &amp; operand )
  85. {
  86. // as above...?
  87. }</programlisting>
  88. </para>
  89. <para>Of course, additional changes are required, for currently if the passed
  90. <code>variant</code> in fact contained a <code>std::complex</code> value,
  91. <code>times_two</code> would silently return -- without any of the desired
  92. side-effects and without any error. In this case, the fix is obvious. But in
  93. more complicated programs, it could take considerable time to identify and
  94. locate the error in the first place.</para>
  95. <para>Thus, real-world use of <code>variant</code> typically demands an access
  96. mechanism more robust than <code>get</code>. For this reason,
  97. <code>variant</code> supports compile-time checked
  98. <emphasis role="bold">visitation</emphasis> via
  99. <code><functionname>apply_visitor</functionname></code>. Visitation requires
  100. that the programmer explicitly handle (or ignore) each bounded type. Failure
  101. to do so results in a compile-time error.</para>
  102. <para>Visitation of a <code>variant</code> requires a visitor object. The
  103. following demonstrates one such implementation of a visitor implementating
  104. behavior identical to <code>times_two</code>:
  105. <programlisting>class times_two_visitor
  106. : public <classname>boost::static_visitor</classname>&lt;&gt;
  107. {
  108. public:
  109. void operator()(int &amp; i) const
  110. {
  111. i *= 2;
  112. }
  113. void operator()(std::string &amp; str) const
  114. {
  115. str += str;
  116. }
  117. };</programlisting>
  118. </para>
  119. <para>With the implementation of the above visitor, we can then apply it to
  120. <code>v</code>, as seen in the following:
  121. <programlisting><functionname>boost::apply_visitor</functionname>( times_two_visitor(), v );</programlisting>
  122. </para>
  123. <para>As expected, the content of <code>v</code> is now a
  124. <code>std::string</code> equal to <code>"hello world! hello world! "</code>.
  125. (We'll skip the verification this time.)</para>
  126. <para>In addition to enhanced robustness, visitation provides another
  127. important advantage over <code>get</code>: the ability to write generic
  128. visitors. For instance, the following visitor will &quot;double&quot; the
  129. content of <emphasis>any</emphasis>&nbsp;<code>variant</code> (provided its
  130. bounded types each support operator+=):
  131. <programlisting>class times_two_generic
  132. : public <classname>boost::static_visitor</classname>&lt;&gt;
  133. {
  134. public:
  135. template &lt;typename T&gt;
  136. void operator()( T &amp; operand ) const
  137. {
  138. operand += operand;
  139. }
  140. };</programlisting>
  141. </para>
  142. <para>Again, <code>apply_visitor</code> sets the wheels in motion:
  143. <programlisting><functionname>boost::apply_visitor</functionname>( times_two_generic(), v );</programlisting>
  144. </para>
  145. <para>While the initial setup costs of visitation may exceed that required for
  146. <code>get</code>, the benefits quickly become significant. Before concluding
  147. this section, we should explore one last benefit of visitation with
  148. <code>apply_visitor</code>:
  149. <emphasis role="bold">delayed visitation</emphasis>. Namely, a special form
  150. of <code>apply_visitor</code> is available that does not immediately apply
  151. the given visitor to any <code>variant</code> but rather returns a function
  152. object that operates on any <code>variant</code> given to it. This behavior
  153. is particularly useful when operating on sequences of <code>variant</code>
  154. type, as the following demonstrates:
  155. <programlisting>std::vector&lt; <classname>boost::variant</classname>&lt;int, std::string&gt; &gt; vec;
  156. vec.push_back( 21 );
  157. vec.push_back( "hello " );
  158. times_two_generic visitor;
  159. std::for_each(
  160. vec.begin(), vec.end()
  161. , <functionname>boost::apply_visitor</functionname>(visitor)
  162. );</programlisting>
  163. </para>
  164. </section>