variant
A type safe generalized union type
variant.hpp
Go to the documentation of this file.
1 /*
2  * Boost Software License - Version 1.0 - August 17th, 2003
3  *
4  * Permission is hereby granted, free of charge, to any person or organization
5  * obtaining a copy of the software and accompanying documentation covered by
6  * this license (the "Software") to use, reproduce, display, distribute,
7  * execute, and transmit the Software, and to prepare derivative works of the
8  * Software, and to permit third-parties to whom the Software is furnished to
9  * do so, all subject to the following:
10  *
11  * The copyright notices in the Software and this entire statement, including
12  * the above license grant, this restriction and the following disclaimer,
13  * must be included in all copies of the Software, in whole or in part, and
14  * all derivative works of the Software, unless such copies or derivative
15  * works are solely in the form of machine-executable object code generated by
16  * a source language processor.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
21  * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
22  * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
23  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24  * DEALINGS IN THE SOFTWARE.
25  *
26  */
27 
28 #ifndef VARIANT_HPP
29 #define VARIANT_HPP
30 
38 #include <cassert>
39 #include <stdexcept>
40 #include <typeinfo>
41 #include <type_traits>
42 #include <utility>
43 
44 #include "variant_storage.hpp"
45 
64 template<class ... Types>
65 class variant;
66 
70 class bad_variant_access : public std::logic_error
71 {
72 public:
73  explicit bad_variant_access(const std::string& arg) : logic_error(arg) { }
74  explicit bad_variant_access(const char* arg) : logic_error(arg) { }
75 
76  virtual ~bad_variant_access() noexcept = default;
77 };
78 
82 template<std::size_t I, class Variant>
84 
91 template<class T, class Variant>
93 
97 template<class Variant>
98 struct variant_size;
99 
100 template<std::size_t I, class Variant>
101 using variant_element_t = typename variant_element<I, Variant>::type;
102 
103 #ifndef VARIANT_IN_DOXYGEN_PARSING
104 
105 template<std::size_t I, class Variant>
106 struct variant_element<I, const Variant>
107 {
108  typedef typename std::add_const< typename variant_element<I, Variant>::type >::type type;
109 };
110 
111 template<std::size_t I, class Variant>
112 struct variant_element<I, volatile Variant>
113 {
114  typedef typename std::add_volatile< typename variant_element<I, Variant>::type >::type type;
115 };
116 
117 template<std::size_t I, class Variant>
118 struct variant_element<I, const volatile Variant>
119 {
120  typedef typename std::add_cv< typename variant_element<I, Variant>::type >::type type;
121 };
122 
123 template<std::size_t I, class Variant>
124 struct variant_element<I, Variant&>
125 {
126  typedef typename std::add_lvalue_reference< typename variant_element<I, Variant>::type >::type type;
127 };
128 
129 template<std::size_t I, class Variant>
130 struct variant_element<I, Variant&&>
131 {
132  typedef typename std::add_rvalue_reference< typename variant_element<I, Variant>::type >::type type;
133 };
134 
135 template<std::size_t I, class Head, class ... Args>
136 struct variant_element<I, variant<Head, Args ... > > :
137  variant_element<I-1, variant<Args...> > {};
138 
139 template<class Head, class ... Args>
140 struct variant_element<0, variant<Head, Args ... > >
141 {
142  typedef Head type;
143 };
144 
145 template<class T, class Variant>
146 struct variant_index<T, const Variant> : variant_index<T, Variant> {};
147 
148 template<class T, class Variant>
149 struct variant_index<T, volatile Variant> : variant_index<T, Variant> {};
150 
151 template<class T, class Variant>
152 struct variant_index<T, const volatile Variant> : variant_index<T, Variant> {};
153 
154 template<class T, class Variant>
155 struct variant_index<T, Variant&> : variant_index<T, Variant> {};
156 
157 template<class T, class Variant>
158 struct variant_index<T, Variant&&> : variant_index<T, Variant> {};
159 
160 template<class T, class Head, class ... Args>
161 struct variant_index<T, variant<Head, Args... > > :
162  std::integral_constant<
163  std::size_t,
164  variant_index<T, variant<Args ...> >::value + 1
165  > {};
166 
167 template<class Head, class ... Args>
168 struct variant_index<Head, variant<Head, Args... > > :
169  std::integral_constant<
170  std::size_t,
171  0
172  > {};
173 
174 template<class ... Args>
175 struct variant_size< variant<Args...> > : std::integral_constant<std::size_t, sizeof ... (Args) > {};
176 
177 template<class Variant>
178 struct variant_size< const Variant > : variant_size< Variant > {};
179 
180 template<class Variant>
181 struct variant_size< volatile Variant > : variant_size< Variant > {};
182 
183 template<class Variant>
184 struct variant_size< const volatile Variant > : variant_size< Variant > {};
185 
186 template<class Variant>
187 struct variant_size< Variant& > : variant_size< Variant > {};
188 
189 template<class Variant>
190 struct variant_size< Variant&& > : variant_size< Variant > {};
191 
192 #endif //VARIANT_IN_DOXYGEN_PARSING
193 
194 template<class ... Types>
195 class variant
196 {
197  template<class ...>
198  struct deduce_overload_helper;
199 
200  template<class Head, class ... Tail>
201  struct deduce_overload_helper<Head, Tail...> : deduce_overload_helper<Tail...>
202  {
203  using deduce_overload_helper<Tail...>::operator();
204 
205  std::integral_constant< std::size_t,
206  sizeof ... (Types) - sizeof ... (Tail) - 1> operator()( Head );
207  };
208 
209  template<class Head>
210  struct deduce_overload_helper<Head>
211  {
212  std::integral_constant< std::size_t, sizeof ... (Types) - 1> operator()( Head );
213  };
214 
235  template<class T, class X = void>
236  struct deduce_overload
237  {
238  static_assert( std::conditional_t<true, std::false_type, X>::value, "Type deduction for T is ambiguous" );
239  };
240 
241  template<class T>
242  struct deduce_overload<
243  T,
244  typename std::enable_if<
245  detail::or_< std::is_same< std::decay_t<T>, Types > ... >::value
246  >::type >
247  {
249  };
250 
251  template<class T>
252  struct deduce_overload<
253  T,
254  typename std::enable_if<
255  !detail::or_< std::is_same< std::decay_t<T>, Types > ... >::value,
256  decltype(std::result_of_t< deduce_overload_helper<Types...>(T) >{}, void() )
257  >::type >
258  {
259  typedef std::result_of_t< deduce_overload_helper<Types...>(T) > type;
260  };
261 
262  template<class T>
263  using deduce_overload_t = typename deduce_overload<T>::type;
264 
265  // Enable testing of deduce_overload
266  friend class variant_deduce_overload_Test;
267 
268  // Checks if N types are all different
269  template<class T, class ... Args>
270  struct all_different :
271  detail::and_<
272  all_different< Args ... >,
273  detail::not_< std::is_same<T,Args> >...
274  > {};
275 
276  template<class T>
277  struct all_different<T> : std::true_type {};
278 
279  template<class ... OtherTypes>
280  static constexpr std::size_t get_which_for( const variant<OtherTypes...> & v) noexcept
281  {
282  constexpr std::size_t which_map[] = { deduce_overload_t<OtherTypes>::value ... };
283  return which_map[ v.which() ];
284  }
285 
286 public:
287 
288  static_assert( all_different< Types ...>::value, "Variant type parameters must be all different" );
289  static_assert( !detail::or_< std::is_reference<Types>... >::value, "Variant type parameters can not be references" );
290  static_assert( !detail::or_< std::is_const<Types>... >::value, "Variant type parameters can not be const" );
291  static_assert( !detail::or_< std::is_volatile<Types>... >::value, "Variant type parameters can not be volatile" );
292  static_assert( sizeof ... (Types) > 0, "Type list can not be empty" );
293 
295  typedef variant_storage< Types ... > storage_t;
296 
302  variant() noexcept( std::is_nothrow_constructible< storage_t, std::integral_constant<std::size_t, 0> >::value ) :
303  which_(0),
304  storage_( std::integral_constant<std::size_t, 0>{} )
305  {}
306 
313  variant( const variant& other )
314  noexcept( detail::and_< std::is_nothrow_copy_constructible<Types> ... >::value ) :
315 
316  which_( other.which_ )
317  {
318  invoke(
319  [&](auto & val)
320  {
321  typedef typename std::remove_reference<decltype(val)>::type T;
322 
323  new (& get< variant_index<T, variant>::value >(storage_)) T(val);
324  },
325  other.storage_,
326  other.which_);
327  }
328 
329  variant( variant & other )
330  noexcept( detail::and_<std::is_nothrow_copy_constructible<Types> ... >::value ) :
331 
332  which_( other.which_ )
333  {
334  invoke(
335  [&](auto & val)
336  {
337  typedef typename std::remove_reference<decltype(val)>::type T;
338 
339  new (& get< variant_index<T, variant>::value >(storage_)) T(val);
340  },
341  other.storage_,
342  other.which_);
343  }
344 
349  variant( variant && other ) noexcept( detail::and_< std::is_nothrow_move_constructible<Types> ... >::value) :
350  which_( other.which_ )
351  {
352  invoke(
353  [&](auto & val)
354  {
355  typedef typename std::remove_reference<decltype(val)>::type T;
356 
357  new (& get< variant_index<T, variant>::value >(storage_)) T(std::move(val) );
358  },
359  other.storage_,
360  other.which_);
361  }
362 
367  template<class ... OtherTypes>
368  variant( const variant<OtherTypes...> & other )
369  noexcept( detail::and_<std::is_nothrow_copy_constructible<OtherTypes> ... >::value ) :
370 
371  which_( get_which_for(other) )
372  {
373  invoke(
374  [&](auto & val)
375  {
376  typedef typename std::remove_reference<decltype(val)>::type T;
377 
378  new (& get< deduce_overload_t<T>::value >(storage_)) T(val);
379  },
380  other.get_storage(),
381  other.which());
382  }
383 
388  template<class ... OtherTypes>
390  noexcept( detail::and_<std::is_nothrow_move_constructible<OtherTypes> ... >::value ) :
391 
392  which_( get_which_for(other) )
393  {
394  invoke(
395  [&](auto && val)
396  {
397  typedef typename std::remove_reference<decltype(val)>::type T;
398 
399  new (& get< deduce_overload_t<T>::value >(storage_)) T(std::move(val));
400  },
401  other.get_storage(),
402  other.which());
403  }
404 
411  template<class T>
412  variant( T && value )
413  noexcept( std::is_nothrow_constructible<
414  storage_t,
415  deduce_overload_t<T&&>,
416  T&&>::value) :
417 
418  which_( deduce_overload_t<T&&>::value ),
419  storage_( deduce_overload_t<T&&>{},
420  std::forward<T>(value) )
421  {}
422 
429  template<class T>
430  variant( std::initializer_list<T> init_list )
431  noexcept( std::is_nothrow_constructible<
432  storage_t,
433  deduce_overload_t< std::initializer_list<T> >,
434  std::initializer_list<T> >::value) :
435 
436  which_( deduce_overload_t< std::initializer_list<T> >::value ),
437  storage_( deduce_overload_t< std::initializer_list<T> >{}, init_list )
438  {}
439 
444  ~variant() noexcept( detail::and_< std::is_nothrow_destructible< Types > ... >::value)
445  {
446  invoke(
447  [&](auto & val)
448  {
449  typedef typename std::remove_reference<decltype(val)>::type T;
450  val.~T();
451  },
452  storage_,
453  which_);
454  }
455 
470  variant& operator=(const variant & other)
471  noexcept( detail::and_ <
472  std::is_nothrow_copy_assignable< Types > ...,
473  std::is_nothrow_copy_constructible< Types > ... >::value)
474  {
475  static_assert( detail::and_< std::is_nothrow_destructible<Types> ... >::value,
476  "Can not implement any exception safety on variant copy assignement if a destructor can throw" );
477 
478  if ( which_ == other.which_ )
479  {
480  invoke(
481  [&](auto & val)
482  {
483  typedef typename std::remove_reference<decltype(val)>::type T;
484 
485  get< variant_index<T, variant>::value >( storage_ ) = val;
486  },
487  other.storage_,
488  other.which_ );
489  }
490  else
491  {
492  constexpr bool is_nothrow_copy_constructible[] = { std::is_nothrow_copy_constructible<Types>::value ... };
493 
494  if ( is_nothrow_copy_constructible[ other.which_ ] )
495  {
496  // Default strategy, nothrow copy constructor, nothing can go wrong.
497  // Just destroy the old one and construct a new one
498  ~variant();
499 
500  new (this) variant( other );
501  }
502  else
503  {
504  // If the move constructor throws, we can not restore the original value
505  static_assert( detail::and_< std::is_nothrow_move_constructible<Types> ... >::value,
506  "Can not implement any exception safety on variant copy assignement if a move constructor can throw" );
507 
508  // Copy and move
509  variant temp( other ); //Even if it throws, strong guarantee
510 
511  // Destroy the old one
512  this->~variant();
513 
514  // Reinitialize
515  new (this) variant( std::move(other) );
516  }
517  }
518  return *this;
519  }
520 
531  variant& operator=(variant && other) noexcept( detail::and_ <std::is_nothrow_move_assignable<Types > ... >::value)
532  {
533  if ( which_ == other.which_ )
534  {
535  invoke(
536  [&](auto & val)
537  {
538  typedef typename std::remove_reference<decltype(val)>::type T;
539 
540  get< variant_index<T, variant>::value >( storage_ ) = std::move(val);
541  },
542  other.storage_,
543  other.which_ );
544  }
545  else
546  {
547  // If the move constructor throws, we can not restore the original value
548  static_assert( detail::and_< std::is_nothrow_move_constructible<Types > ... >::value,
549  "Can not implement any exception safety on variant move assignement if a move constructor can throw" );
550 
551  static_assert( detail::and_< std::is_nothrow_destructible<Types > ... >::value,
552  "Can not implement any exception safety on variant move assignement if a destructor can throw" );
553 
554  // Destroy the old one
555  this->~variant();
556 
557  // Move in
558  new (this) variant( std::move(other) );
559  }
560  return *this;
561  }
562 
566  std::size_t which() const noexcept { return which_; }
567 
571  const std::type_info & type() const noexcept
572  {
573  return invoke(
574  [&](auto & val) -> const std::type_info &
575  {
576  return typeid(typename std::remove_reference<decltype(val)>::type);
577  },
578  storage_,
579  which_);
580  }
581 
588  template<class T, class Variant, class = typename
589  std::enable_if<
590  std::is_same< variant, typename std::decay< Variant >::type >::value
591  >::type >
592  friend variant_element_t< variant_index<T, Variant>::value, Variant > get(Variant && v)
593  {
594  if ( v.which() != variant_index<T, variant>::value )
595  throw bad_variant_access("bad_variant_access");
596  return get< variant_index<T, variant>::value >(std::forward<Variant>(v).storage_);
597  }
598 
599  storage_t& get_storage() & noexcept { return storage_; }
600  storage_t&& get_storage() && noexcept { return std::move(storage_); }
601  const storage_t& get_storage() const & noexcept { return storage_; }
602 
603 private:
604  std::size_t which_;
605  storage_t storage_;
606 };
607 
608 
609 #endif //VARIANT_HPP
variant(variant< OtherTypes...> &&other) noexcept(detail::and_< std::is_nothrow_move_constructible< OtherTypes >... >::value)
Move from a compatible variant.
Definition: variant.hpp:389
variant(const variant &other) noexcept(detail::and_< std::is_nothrow_copy_constructible< Types >... >::value)
Copy constructor, invokes the copy constructor of the type currently bound on other.
Definition: variant.hpp:313
Obtain the index of a variant type.
Definition: variant.hpp:92
Exception class thrown when a type that is not currently bound is accessed via variant::get.
Definition: variant.hpp:70
variant(std::initializer_list< T > init_list) noexcept(std::is_nothrow_constructible< storage_t, deduce_overload_t< std::initializer_list< T > >, std::initializer_list< T > >::value)
Initializer list constructor, tries to deduce the desired type.
Definition: variant.hpp:430
variant_storage< Types... > storage_t
The underlying variant_storage type.
Definition: variant.hpp:288
Implements the class variant_storage.
std::size_t which() const noexcept
Return the index of the current bound object.
Definition: variant.hpp:566
variant & operator=(variant &&other) noexcept(detail::and_< std::is_nothrow_move_assignable< Types >... >::value)
Move assignment operator.
Definition: variant.hpp:531
~variant() noexcept(detail::and_< std::is_nothrow_destructible< Types >... >::value)
Destructor, calls the destructor on the currently bound type.
Definition: variant.hpp:444
Defines the member type type to the Ith type of the variant.
Definition: variant.hpp:83
Definition: relational.hpp:33
A templated generalized union.
Definition: variant_storage.hpp:64
const std::type_info & type() const noexcept
Return the type_info of the current bound object.
Definition: variant.hpp:571
A type safe generalized union.
Definition: variant.hpp:65
variant(const variant< OtherTypes...> &other) noexcept(detail::and_< std::is_nothrow_copy_constructible< OtherTypes >... >::value)
Copy from a compatible variant.
Definition: variant.hpp:368
Provides access to the number of elements in a variant as a compile-time constant expression...
Definition: variant.hpp:98
variant() noexcept(std::is_nothrow_constructible< storage_t, std::integral_constant< std::size_t, 0 > >::value)
Default construct this variant using the first type in Types.
Definition: variant.hpp:302
variant(T &&value) noexcept(std::is_nothrow_constructible< storage_t, deduce_overload_t< T && >, T && >::value)
Generic constructor, tries to deduce the desired type.
Definition: variant.hpp:412
decltype(auto) invoke(Callable &&c, VariantStorage &&v, std::size_t which) noexcept(noexcept(invoke_variant_storage_t{}(std::forward< Callable >(c), std::forward< VariantStorage >(v), which)))
Calls the provided Callable with an element of the supplied variant storage.
Definition: variant_storage.hpp:386
variant(variant &&other) noexcept(detail::and_< std::is_nothrow_move_constructible< Types >... >::value)
Move constructor, invokes the move constructor of the type currently bound on other.
Definition: variant.hpp:349
variant & operator=(const variant &other) noexcept(detail::and_< std::is_nothrow_copy_assignable< Types >..., std::is_nothrow_copy_constructible< Types >... >::value)
Copy assignment operator.
Definition: variant.hpp:470