123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- <?xml version="1.0" encoding="utf-8"?>
- <!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
- "http://www.boost.org/tools/boostbook/dtd/boostbook.dtd">
- <!--
- Copyright 2003, Eric Friedman, Itay Maman.
- Distributed under 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)
- -->
- <section id="variant.tutorial.basic">
- <title>Basic Usage</title>
- <using-namespace name="boost"/>
- <using-class name="boost::variant"/>
- <para>A discriminated union container on some set of types is defined by
- instantiating the <code><classname>boost::variant</classname></code> class
- template with the desired types. These types are called
- <emphasis role="bold">bounded types</emphasis> and are subject to the
- requirements of the
- <link linkend="variant.concepts.bounded-type"><emphasis>BoundedType</emphasis></link>
- concept. Any number of bounded types may be specified, up to some
- implementation-defined limit (see
- <code><macroname>BOOST_VARIANT_LIMIT_TYPES</macroname></code>).</para>
- <para>For example, the following declares a discriminated union container on
- <code>int</code> and <code>std::string</code>:
- <programlisting><classname>boost::variant</classname>< int, std::string > v;</programlisting>
- </para>
- <para>By default, a <code>variant</code> default-constructs its first
- bounded type, so <code>v</code> initially contains <code>int(0)</code>. If
- this is not desired, or if the first bounded type is not
- default-constructible, a <code>variant</code> can be constructed
- directly from any value convertible to one of its bounded types. Similarly,
- a <code>variant</code> can be assigned any value convertible to one of its
- bounded types, as demonstrated in the following:
- <programlisting>v = "hello";</programlisting>
- </para>
- <para>Now <code>v</code> contains a <code>std::string</code> equal to
- <code>"hello"</code>. We can demonstrate this by
- <emphasis role="bold">streaming</emphasis> <code>v</code> to standard
- output:
- <programlisting>std::cout << v << std::endl;</programlisting>
- </para>
- <para>Usually though, we would like to do more with the content of a
- <code>variant</code> than streaming. Thus, we need some way to access the
- contained value. There are two ways to accomplish this:
- <code><functionname>apply_visitor</functionname></code>, which is safest
- and very powerful, and
- <code><functionname>get</functionname><T></code>, which is
- sometimes more convenient to use.</para>
- <para>For instance, suppose we wanted to concatenate to the string contained
- in <code>v</code>. With <emphasis role="bold">value retrieval</emphasis>
- by <code><functionname>get</functionname></code>, this may be accomplished
- quite simply, as seen in the following:
- <programlisting>std::string& str = <functionname>boost::get</functionname><std::string>(v);
- str += " world! ";</programlisting>
- </para>
- <para>As desired, the <code>std::string</code> contained by <code>v</code> now
- is equal to <code>"hello world! "</code>. Again, we can demonstrate this by
- streaming <code>v</code> to standard output:
- <programlisting>std::cout << v << std::endl;</programlisting>
- </para>
- <para>While use of <code>get</code> is perfectly acceptable in this trivial
- example, <code>get</code> generally suffers from several significant
- shortcomings. For instance, if we were to write a function accepting a
- <code>variant<int, std::string></code>, we would not know whether
- the passed <code>variant</code> contained an <code>int</code> or a
- <code>std::string</code>. If we insisted upon continued use of
- <code>get</code>, we would need to query the <code>variant</code> for its
- contained type. The following function, which "doubles" the
- content of the given <code>variant</code>, demonstrates this approach:
- <programlisting>void times_two( boost::variant< int, std::string > & operand )
- {
- if ( int* pi = <functionname>boost::get</functionname><int>( &operand ) )
- *pi *= 2;
- else if ( std::string* pstr = <functionname>boost::get</functionname><std::string>( &operand ) )
- *pstr += *pstr;
- }</programlisting>
- </para>
- <para>However, such code is quite brittle, and without careful attention will
- likely lead to the introduction of subtle logical errors detectable only at
- runtime. For instance, consider if we wished to extend
- <code>times_two</code> to operate on a <code>variant</code> with additional
- bounded types. Specifically, let's add
- <code>std::complex<double></code> to the set. Clearly, we would need
- to at least change the function declaration:
- <programlisting>void times_two( boost::variant< int, std::string, std::complex<double> > & operand )
- {
- // as above...?
- }</programlisting>
- </para>
- <para>Of course, additional changes are required, for currently if the passed
- <code>variant</code> in fact contained a <code>std::complex</code> value,
- <code>times_two</code> would silently return -- without any of the desired
- side-effects and without any error. In this case, the fix is obvious. But in
- more complicated programs, it could take considerable time to identify and
- locate the error in the first place.</para>
- <para>Thus, real-world use of <code>variant</code> typically demands an access
- mechanism more robust than <code>get</code>. For this reason,
- <code>variant</code> supports compile-time checked
- <emphasis role="bold">visitation</emphasis> via
- <code><functionname>apply_visitor</functionname></code>. Visitation requires
- that the programmer explicitly handle (or ignore) each bounded type. Failure
- to do so results in a compile-time error.</para>
- <para>Visitation of a <code>variant</code> requires a visitor object. The
- following demonstrates one such implementation of a visitor implementating
- behavior identical to <code>times_two</code>:
- <programlisting>class times_two_visitor
- : public <classname>boost::static_visitor</classname><>
- {
- public:
- void operator()(int & i) const
- {
- i *= 2;
- }
- void operator()(std::string & str) const
- {
- str += str;
- }
- };</programlisting>
- </para>
- <para>With the implementation of the above visitor, we can then apply it to
- <code>v</code>, as seen in the following:
- <programlisting><functionname>boost::apply_visitor</functionname>( times_two_visitor(), v );</programlisting>
- </para>
- <para>As expected, the content of <code>v</code> is now a
- <code>std::string</code> equal to <code>"hello world! hello world! "</code>.
- (We'll skip the verification this time.)</para>
- <para>In addition to enhanced robustness, visitation provides another
- important advantage over <code>get</code>: the ability to write generic
- visitors. For instance, the following visitor will "double" the
- content of <emphasis>any</emphasis> <code>variant</code> (provided its
- bounded types each support operator+=):
- <programlisting>class times_two_generic
- : public <classname>boost::static_visitor</classname><>
- {
- public:
- template <typename T>
- void operator()( T & operand ) const
- {
- operand += operand;
- }
- };</programlisting>
- </para>
- <para>Again, <code>apply_visitor</code> sets the wheels in motion:
- <programlisting><functionname>boost::apply_visitor</functionname>( times_two_generic(), v );</programlisting>
- </para>
- <para>While the initial setup costs of visitation may exceed that required for
- <code>get</code>, the benefits quickly become significant. Before concluding
- this section, we should explore one last benefit of visitation with
- <code>apply_visitor</code>:
- <emphasis role="bold">delayed visitation</emphasis>. Namely, a special form
- of <code>apply_visitor</code> is available that does not immediately apply
- the given visitor to any <code>variant</code> but rather returns a function
- object that operates on any <code>variant</code> given to it. This behavior
- is particularly useful when operating on sequences of <code>variant</code>
- type, as the following demonstrates:
- <programlisting>std::vector< <classname>boost::variant</classname><int, std::string> > vec;
- vec.push_back( 21 );
- vec.push_back( "hello " );
- times_two_generic visitor;
- std::for_each(
- vec.begin(), vec.end()
- , <functionname>boost::apply_visitor</functionname>(visitor)
- );</programlisting>
- </para>
- </section>
|