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