1 // The Art of C++ / Tuple
2 // Copyright (c) 2015-2016 Daniel Frey
3 
4 #ifndef TAOCPP_INCLUDE_TUPLE_TUPLE_HPP
5 #define TAOCPP_INCLUDE_TUPLE_TUPLE_HPP
6 
7 #include "../seq/config.hpp"
8 #include "../seq/integer_sequence.hpp"
9 #include "../seq/is_all.hpp"
10 #include "../seq/type_by_index.hpp"
11 #include "../seq/sum.hpp"
12 #include "../seq/make_integer_sequence.hpp"
13 #include "../seq/inclusive_scan.hpp"
14 #include "../seq/minus.hpp"
15 #include "../seq/map.hpp"
16 #include "../seq/exclusive_scan.hpp"
17 
18 #include <type_traits>
19 #include <utility>
20 #include <memory>
21 
22 #if (__cplusplus >= 201402L)
23 #define TAOCPP_TUPLE_CONSTEXPR constexpr
24 #else
25 #define TAOCPP_TUPLE_CONSTEXPR
26 #endif
27 
28 namespace tao
29 {
30   template< typename... Ts >
31   struct tuple;
32 }
33 
34 namespace std
35 {
36   // 20.4.2.8 Tuple traits [tuple.traits]
37 
38   template< typename... Ts, typename A >
39   struct uses_allocator< tao::tuple< Ts... >, A > : true_type {};
40 }
41 
42 namespace tao
43 {
44   template< std::size_t I, typename... Ts >
45   TAOCPP_TUPLE_CONSTEXPR
46   const seq::type_by_index_t< I, Ts... >& get( const tuple< Ts... >& ) noexcept;
47 
48   template< std::size_t I, typename... Ts >
49   TAOCPP_TUPLE_CONSTEXPR
50   seq::type_by_index_t< I, Ts... >& get( tuple< Ts... >& ) noexcept;
51 
52   template< std::size_t I, typename... Ts >
53   TAOCPP_TUPLE_CONSTEXPR
54   seq::type_by_index_t< I, Ts... >&& get( const tuple< Ts... >&& ) noexcept;
55 
56   namespace impl
57   {
58     // TODO: std::pair support
59     // TODO: allocator support
60 
61     using swallow = bool[];
62 
63     template< typename T, typename >
64     struct dependent_type : T {};
65 
66     template< bool B, typename T = void >
67     using enable_if_t = typename std::enable_if< B, T >::type;
68 
69     // TODO: using std::swap?
70     template< typename T >
71     using is_nothrow_swappable = std::integral_constant< bool, noexcept( swap( std::declval< T& >(), std::declval< T& >() ) ) >;
72 
73 #if __cplusplus >= 201402L
74     template< typename T >
75     using is_final = std::is_final< T >;
76 #else
77     template< typename T >
78     using is_final = std::integral_constant< bool, __is_final( T ) >;
79 #endif
80 
81     template< bool, bool >
82     struct uses_alloc_ctor;
83 
84     template< typename T, typename A, typename... As >
85     using uses_alloc_ctor_t = uses_alloc_ctor< std::uses_allocator< T, A >::value, std::is_constructible< T, std::allocator_arg_t, A, As... >::value >*;
86 
87     template< std::size_t I, typename T, bool = std::is_empty< T >::value && !is_final< T >::value >
88     struct tuple_value
89     {
90       T value;
91 
tuple_valuetao::impl::tuple_value92       constexpr tuple_value()
93         noexcept( std::is_nothrow_default_constructible< T >::value )
94         : value()
95       {
96         static_assert( !std::is_reference< T >::value, "attempted to default construct a reference element in a tuple" );
97       }
98 
99       template< bool B, typename A >
tuple_valuetao::impl::tuple_value100       tuple_value( uses_alloc_ctor< false, B >*, const A& )
101         : value()
102       {
103         static_assert( !std::is_reference< T >::value, "attempted to default construct a reference element in a tuple" );
104       }
105 
106       template< typename A >
tuple_valuetao::impl::tuple_value107       tuple_value( uses_alloc_ctor< true, true >*, const A& a )
108         : value( std::allocator_arg_t(), a )
109       {
110         static_assert( !std::is_reference< T >::value, "attempted to default construct a reference element in a tuple" );
111       }
112 
113       template< typename A >
tuple_valuetao::impl::tuple_value114       tuple_value( uses_alloc_ctor< true, false >*, const A& a )
115         : value( a )
116       {
117         static_assert( !std::is_reference< T >::value, "attempted to default construct a reference element in a tuple" );
118       }
119 
120       template< typename U,
121                 typename = impl::enable_if_t< !std::is_same< typename std::decay< U >::type, tuple_value >::value >,
122                 typename = impl::enable_if_t< std::is_constructible< T, U >::value > >
123       TAOCPP_TUPLE_CONSTEXPR
tuple_valuetao::impl::tuple_value124       explicit tuple_value( U&& v )
125         noexcept( std::is_nothrow_constructible< T, U >::value )
126         : value( std::forward< U >( v ) )
127       {}
128 
129       template< bool B, typename A, typename U >
tuple_valuetao::impl::tuple_value130       tuple_value( uses_alloc_ctor< false, B >*, const A&, U&& v )
131         : value( std::forward< U >( v ) )
132       {
133         // TODO: Add check for rvalue to lvalue reference
134       }
135 
136       template< typename A, typename U >
tuple_valuetao::impl::tuple_value137       tuple_value( uses_alloc_ctor< true, true >*, const A& a, U&& v )
138         : value( std::allocator_arg_t(), a, std::forward< U >( v ) )
139       {
140         // TODO: Add check for rvalue to lvalue reference
141       }
142 
143       template< typename A, typename U >
tuple_valuetao::impl::tuple_value144       tuple_value( uses_alloc_ctor< true, false >*, const A& a, U&& v )
145         : value( std::forward< U >( v ), a )
146       {
147         // TODO: Add check for rvalue to lvalue reference
148       }
149 
150       tuple_value( const tuple_value& ) = default;
151       tuple_value( tuple_value&& ) = default;
152 
153       template< typename U >
operator =tao::impl::tuple_value154       tuple_value& operator=( U&& v )
155         noexcept( std::is_nothrow_assignable< T&, U >::value )
156       {
157         value = std::forward< U >( v );
158         return *this;
159       }
160 
swaptao::impl::tuple_value161       void swap( tuple_value& v )
162         noexcept( is_nothrow_swappable< T >::value )
163       {
164         using std::swap;
165         swap( value, v.value );
166       }
167 
168       TAOCPP_TUPLE_CONSTEXPR
gettao::impl::tuple_value169       T& get() noexcept
170       {
171         return value;
172       }
173 
174       TAOCPP_TUPLE_CONSTEXPR
gettao::impl::tuple_value175       const T& get() const noexcept
176       {
177         return value;
178       }
179     };
180 
181     template< std::size_t I, typename T >
182     struct tuple_value< I, T, true >
183       : private T
184     {
tuple_valuetao::impl::tuple_value185       constexpr tuple_value()
186         noexcept( std::is_nothrow_default_constructible< T >::value )
187         : T()
188       {}
189 
190       template< bool B, typename A >
tuple_valuetao::impl::tuple_value191       tuple_value( uses_alloc_ctor< false, B >*, const A& )
192         : T()
193       {}
194 
195       template< typename A >
tuple_valuetao::impl::tuple_value196       tuple_value( uses_alloc_ctor< true, true >*, const A& a )
197         : T( std::allocator_arg_t(), a )
198       {}
199 
200       template< typename A >
tuple_valuetao::impl::tuple_value201       tuple_value( uses_alloc_ctor< true, false >*, const A& a )
202         : T( a )
203       {}
204 
205       template< typename U,
206                 typename = impl::enable_if_t< !std::is_same< typename std::decay< U >::type, tuple_value >::value >,
207                 typename = impl::enable_if_t< std::is_constructible< T, U >::value > >
208       TAOCPP_TUPLE_CONSTEXPR
tuple_valuetao::impl::tuple_value209       explicit tuple_value( U&& v )
210         noexcept( std::is_nothrow_constructible< T, U >::value )
211         : T( std::forward< U >( v ) )
212       {}
213 
214       template< bool B, typename A, typename U >
tuple_valuetao::impl::tuple_value215       tuple_value( uses_alloc_ctor< false, B >*, const A&, U&& v )
216         : T( std::forward< U >( v ) )
217       {}
218 
219       template< typename A, typename U >
tuple_valuetao::impl::tuple_value220       tuple_value( uses_alloc_ctor< true, true >*, const A& a, U&& v )
221         : T( std::allocator_arg_t(), a, std::forward< U >( v ) )
222       {}
223 
224       template< typename A, typename U >
tuple_valuetao::impl::tuple_value225       tuple_value( uses_alloc_ctor< true, false >*, const A& a, U&& v )
226         : T( std::forward< U >( v ), a )
227       {}
228 
229       tuple_value( const tuple_value& ) = default;
230       tuple_value( tuple_value&& ) = default;
231 
232       template< typename U >
operator =tao::impl::tuple_value233       tuple_value& operator=( U&& v )
234         noexcept( std::is_nothrow_assignable< T&, U >::value )
235       {
236         T::operator=( std::forward< U >( v ) );
237         return *this;
238       }
239 
swaptao::impl::tuple_value240       void swap( tuple_value& v )
241         noexcept( is_nothrow_swappable< T >::value )
242       {
243         using std::swap;
244         swap( *this, v );
245       }
246 
247       TAOCPP_TUPLE_CONSTEXPR
gettao::impl::tuple_value248       T& get() noexcept
249       {
250         return static_cast< T& >( *this );
251       }
252 
253       TAOCPP_TUPLE_CONSTEXPR
gettao::impl::tuple_value254       const T& get() const noexcept
255       {
256         return static_cast< const T& >( *this );
257       }
258     };
259 
260     template< typename, typename... >
261     struct tuple_base;
262 
263     template< std::size_t... Is, typename... Ts >
264     struct tuple_base< seq::index_sequence< Is... >, Ts... >
265       : tuple_value< Is, Ts >...
266     {
267       constexpr tuple_base() = default;
268 
269       template< typename... Us >
270       TAOCPP_TUPLE_CONSTEXPR
tuple_basetao::impl::tuple_base271       explicit tuple_base( Us&&... us )
272         : tuple_value< Is, Ts >( std::forward< Us >( us ) )...
273       {}
274 
275       template< typename A, typename... Us >
276       TAOCPP_TUPLE_CONSTEXPR
tuple_basetao::impl::tuple_base277       tuple_base( std::allocator_arg_t, const A& a, Us&&... us )
278         : tuple_value< Is, Ts >( uses_alloc_ctor_t< Ts, A, Us >(), a, std::forward< Us >( us ) )...
279       {}
280 
281       tuple_base( const tuple_base& ) = default;
282       tuple_base( tuple_base&& ) = default;
283 
operator =tao::impl::tuple_base284       tuple_base& operator=( const tuple_base& v )
285         noexcept( seq::is_all< std::is_nothrow_copy_assignable< Ts >::value... >::value )
286       {
287 #ifdef TAOCPP_FOLD_EXPRESSIONS
288         ( tuple_value< Is, Ts >::operator=( static_cast< tuple_value< Is, Ts >& >( v ).get() ), ... );
289 #else
290         (void)swallow{ ( tuple_value< Is, Ts >::operator=( static_cast< tuple_value< Is, Ts >& >( v ).get() ), true )..., true };
291 #endif
292         return *this;
293       }
294 
operator =tao::impl::tuple_base295       tuple_base& operator=( tuple_base&& v )
296         noexcept( seq::is_all< std::is_nothrow_move_assignable< Ts >::value... >::value )
297       {
298 #ifdef TAOCPP_FOLD_EXPRESSIONS
299         ( tuple_value< Is, Ts >::operator=( std::forward< Ts >( static_cast< tuple_value< Is, Ts >& >( v ).get() ) ), ... );
300 #else
301         (void)swallow{ ( tuple_value< Is, Ts >::operator=( static_cast< tuple_value< Is, Ts >& >( v ) ), true )..., true };
302 #endif
303         return *this;
304       }
305 
306       template< typename... Us >
operator =tao::impl::tuple_base307       tuple_base& operator=( const tuple< Us... >& v )
308         noexcept( seq::is_all< std::is_nothrow_assignable< Ts&, const Us& >::value... >::value )
309       {
310 #ifdef TAOCPP_FOLD_EXPRESSIONS
311         ( tuple_value< Is, Ts >::operator=( get< Is >( v ) ), ... );
312 #else
313         (void)swallow{ ( tuple_value< Is, Ts >::operator=( get< Is >( v ) ), true )..., true };
314 #endif
315         return *this;
316       }
317 
318       template< typename... Us >
operator =tao::impl::tuple_base319       tuple_base& operator=( tuple< Us... >&& v )
320         noexcept( seq::is_all< std::is_nothrow_assignable< Ts&, Us&& >::value... >::value )
321       {
322 #ifdef TAOCPP_FOLD_EXPRESSIONS
323         ( tuple_value< Is, Ts >::operator=( get< Is >( std::move( v ) ) ), ... );
324 #else
325         (void)swallow{ ( tuple_value< Is, Ts >::operator=( get< Is >( std::move( v ) ) ), true )..., true };
326 #endif
327         return *this;
328       }
329 
swaptao::impl::tuple_base330       void swap( tuple_base& v )
331         noexcept( seq::is_all< impl::is_nothrow_swappable< Ts >::value... >::value )
332       {
333 #ifdef TAOCPP_FOLD_EXPRESSIONS
334         ( static_cast< tuple_value< Is, Ts >& >( *this ).swap( static_cast< tuple_value< Is, Ts >& >( v ) ), ... );
335 #else
336         (void)swallow{ ( static_cast< tuple_value< Is, Ts >& >( *this ).swap( static_cast< tuple_value< Is, Ts >& >( v ) ), true )..., true };
337 #endif
338       }
339     };
340   }
341 
342   // 20.4.2 Class template tuple [tuple.tuple]
343 
344   // tuple
345   template< typename... Ts >
346   struct tuple
347   {
348   private:
349     using base_t = impl::tuple_base< seq::index_sequence_for< Ts... >, Ts... >;
350     base_t base;
351 
352     template< std::size_t I, typename... Us >
353     friend TAOCPP_TUPLE_CONSTEXPR
354     const seq::type_by_index_t< I, Us... >& get( const tuple< Us... >& ) noexcept;
355 
356     template< std::size_t I, typename... Us >
357     friend TAOCPP_TUPLE_CONSTEXPR
358     seq::type_by_index_t< I, Us... >& get( tuple< Us... >& ) noexcept;
359 
360     template< std::size_t I, typename... Us >
361     friend TAOCPP_TUPLE_CONSTEXPR
362     seq::type_by_index_t< I, Us... >&& get( tuple< Us... >&& ) noexcept;
363 
364   public:
365     // 20.4.2.1 Construction [tuple.cnstr]
366 
367     // TODO: Move this templated condition to base?
368     template< typename dummy = void,
369               typename = impl::enable_if_t< seq::is_all< impl::dependent_type< std::is_default_constructible< Ts >, dummy >::value... >::value > >
tupletao::tuple370     constexpr tuple()
371       noexcept( seq::is_all< std::is_nothrow_default_constructible< Ts >::value... >::value )
372       : base()
373     {}
374 
375     template< typename dummy = void,
376               typename = impl::enable_if_t< seq::is_all< impl::dependent_type< std::is_copy_constructible< Ts >, dummy >::value... >::value > >
377     TAOCPP_TUPLE_CONSTEXPR
tupletao::tuple378     explicit tuple( const Ts&... ts )
379       noexcept( seq::is_all< std::is_nothrow_copy_constructible< Ts >::value... >::value )
380       : base( ts... )
381     {}
382 
383     template< typename... Us,
384               typename = impl::enable_if_t< sizeof...( Us ) == sizeof...( Ts ) >,
385               typename = impl::enable_if_t< seq::is_all< std::is_constructible< Ts, Us&& >::value... >::value > >
386     TAOCPP_TUPLE_CONSTEXPR
tupletao::tuple387     explicit tuple( Us&&... us )
388       noexcept( seq::is_all< std::is_nothrow_constructible< Ts, Us&& >::value... >::value )
389       : base( std::forward< Us >( us )... )
390     {}
391 
392     tuple( const tuple& ) = default;
393     tuple( tuple&& ) = default;
394 
395     template< typename... Us,
396               typename = impl::enable_if_t< sizeof...( Us ) == sizeof...( Ts ) >,
397               typename = impl::enable_if_t< seq::is_all< std::is_constructible< Ts, const Us& >::value... >::value > >
398     TAOCPP_TUPLE_CONSTEXPR
tupletao::tuple399     explicit tuple( const tuple< Us... >& v )
400       noexcept( seq::is_all< std::is_nothrow_constructible< Ts, const Us& >::value... >::value )
401       : base( v )
402     {}
403 
404     template< typename... Us,
405               typename = impl::enable_if_t< sizeof...( Us ) == sizeof...( Ts ) >,
406               typename = impl::enable_if_t< seq::is_all< std::is_constructible< Ts, Us&& >::value... >::value > >
407     TAOCPP_TUPLE_CONSTEXPR
tupletao::tuple408     explicit tuple( tuple< Us... >&& v )
409       noexcept( seq::is_all< std::is_nothrow_constructible< Ts, Us&& >::value... >::value )
410       : base( std::move( v ) )
411     {}
412 
413     template< typename A,
414               typename dummy = void,
415               typename = impl::enable_if_t< seq::is_all< impl::dependent_type< std::is_default_constructible< Ts >, dummy >::value... >::value > >
tupletao::tuple416     tuple( std::allocator_arg_t, const A& a )
417       : base( std::allocator_arg_t(), a )
418     {}
419 
420     template< typename A,
421               typename dummy = void,
422               typename = impl::enable_if_t< seq::is_all< impl::dependent_type< std::is_copy_constructible< Ts >, dummy >::value... >::value > >
tupletao::tuple423     tuple( std::allocator_arg_t, const A& a, const Ts&... ts )
424       : base( std::allocator_arg_t(), a, ts... )
425     {}
426 
427     template< typename A,
428               typename... Us,
429               typename = impl::enable_if_t< sizeof...( Us ) == sizeof...( Ts ) >,
430               typename = impl::enable_if_t< seq::is_all< std::is_constructible< Ts, Us&& >::value... >::value > >
tupletao::tuple431     tuple( std::allocator_arg_t, const A& a, Us&&... us )
432       : base( std::allocator_arg_t(), a, std::forward< Us >( us )... )
433     {}
434 
435     template< typename A >
tupletao::tuple436     tuple( std::allocator_arg_t, const A& a, const tuple& v )
437       : base( std::allocator_arg_t(), a, v )
438     {}
439 
440     template< typename A >
tupletao::tuple441     tuple( std::allocator_arg_t, const A& a, tuple&& v )
442       : base( std::allocator_arg_t(), a, std::move( v ) )
443     {}
444 
445     template< typename A,
446               typename... Us,
447               typename = impl::enable_if_t< sizeof...( Us ) == sizeof...( Ts ) >,
448               typename = impl::enable_if_t< seq::is_all< std::is_constructible< Ts, const Us& >::value... >::value > >
tupletao::tuple449     tuple( std::allocator_arg_t, const A& a, const tuple< Us... >& v )
450       : base( std::allocator_arg_t(), a, v )
451     {}
452 
453     template< typename A,
454               typename... Us,
455               typename = impl::enable_if_t< sizeof...( Us ) == sizeof...( Ts ) >,
456               typename = impl::enable_if_t< seq::is_all< std::is_constructible< Ts, Us&& >::value... >::value > >
tupletao::tuple457     tuple( std::allocator_arg_t, const A& a, tuple< Us... >&& v )
458       : base( std::allocator_arg_t(), a, std::move( v ) )
459     {}
460 
461     // 20.4.2.2 Assignment [tuple.assign]
462 
463     template< typename T,
464               typename = impl::enable_if_t< std::is_assignable< base_t&, T >::value > >
operator =tao::tuple465     tuple& operator=( T&& v )
466       noexcept( std::is_nothrow_assignable< base_t&, T >::value )
467     {
468       base = std::forward< T >( v );
469       return *this;
470     }
471 
472     // 20.4.2.3 swap [tuple.swap]
473 
swaptao::tuple474     void swap( tuple& v )
475       noexcept( noexcept( base.swap( v.base ) ) )
476     {
477       base.swap( v.base );
478     }
479   };
480 
481   template<>
482   struct tuple<>
483   {
tupletao::tuple484     constexpr tuple() noexcept {}
tupletao::tuple485     template< typename A > tuple( std::allocator_arg_t, const A& ) noexcept {}
tupletao::tuple486     template< typename A > tuple( std::allocator_arg_t, const A&, const tuple& ) noexcept {}
swaptao::tuple487     void swap( tuple& ) noexcept {}
488   };
489 
490   // 20.4.2.4 Tuple creation functions [tuple.creation]
491 
492   // ignore helper
493   namespace impl
494   {
495     struct ignore_t
496     {
497       template< typename U >
operator =tao::impl::ignore_t498       ignore_t& operator=( U&& )
499       {
500         return *this;
501       }
502     };
503   }
504 
505   // ignore
506   const impl::ignore_t ignore{};
507 
508   // make_tuple helper
509   namespace impl
510   {
511     template< typename T >
512     struct make_tuple_return
513     {
514       using type = T;
515     };
516 
517     template< typename T >
518     struct make_tuple_return< std::reference_wrapper< T > >
519     {
520       using type = T&;
521     };
522 
523     template< typename T >
524     using make_tuple_return_t = typename make_tuple_return< T >::type;
525   }
526 
527   // make_tuple
528   template< typename... Ts, typename R = tuple< impl::make_tuple_return_t< typename std::decay< Ts >::type >... > >
529   TAOCPP_TUPLE_CONSTEXPR
make_tuple(Ts &&...ts)530   R make_tuple( Ts&&... ts )
531   {
532     return R( std::forward< Ts >( ts )... );
533   }
534 
535   // forward_as_tuple
536   template< typename... Ts >
537   TAOCPP_TUPLE_CONSTEXPR
forward_as_tuple(Ts &&...ts)538   tuple< Ts&&... > forward_as_tuple( Ts&&... ts ) noexcept
539   {
540     return tuple< Ts&&... >( std::forward< Ts >( ts )... );
541   }
542 
543   // tie
544   template< typename... Ts >
545   TAOCPP_TUPLE_CONSTEXPR
tie(Ts &...ts)546   tuple< Ts&... > tie( Ts&... ts ) noexcept
547   {
548     return tuple< Ts&... >( ts... );
549   }
550 
551   // tuple_cat is found at the end, as it requires access to tuple_element_t and get<I>
552 
553   // 20.4.2.5 Tuple helper classes [tuple.helper]
554 
555   // tuple_size
556   template< typename T > struct tuple_size;
557   template< typename T > struct tuple_size< const T > : tuple_size< T > {};
558   template< typename T > struct tuple_size< volatile T > : tuple_size< T > {};
559   template< typename T > struct tuple_size< const volatile T > : tuple_size< T > {};
560 
561   template< typename... Ts >
562   struct tuple_size< tuple< Ts... > >
563     : std::integral_constant< std::size_t, sizeof...( Ts ) >
564   {};
565 
566   // tuple_element
567   template< std::size_t I, typename T > struct tuple_element;
568   template< std::size_t I, typename T > struct tuple_element< I, const T > : tuple_element< I, T > {};
569   template< std::size_t I, typename T > struct tuple_element< I, volatile T > : tuple_element< I, T > {};
570   template< std::size_t I, typename T > struct tuple_element< I, const volatile T > : tuple_element< I, T > {};
571 
572   template< std::size_t I, typename... Ts >
573   struct tuple_element< I, tuple< Ts... > >
574     : seq::type_by_index< I, Ts... >
575   {};
576 
577 #if __cplusplus >= 201402L
578   template< std::size_t I, typename T >
579   using tuple_element_t = typename tuple_element< I, T >::type;
580 #endif
581 
582   // 20.4.2.6 Element access [tuple.elem]
583 
584   // get<I>
585   template< std::size_t I, typename... Ts >
586   TAOCPP_TUPLE_CONSTEXPR
get(const tuple<Ts...> & v)587   const seq::type_by_index_t< I, Ts... >& get( const tuple< Ts... >& v ) noexcept
588   {
589     return static_cast< const impl::tuple_value< I, seq::type_by_index_t< I, Ts... > >& >( v.base ).get();
590   }
591 
592   template< std::size_t I, typename... Ts >
593   TAOCPP_TUPLE_CONSTEXPR
get(tuple<Ts...> & v)594   seq::type_by_index_t< I, Ts... >& get( tuple< Ts... >& v ) noexcept
595   {
596     return static_cast< impl::tuple_value< I, seq::type_by_index_t< I, Ts... > >& >( v.base ).get();
597   }
598 
599   template< std::size_t I, typename... Ts >
600   TAOCPP_TUPLE_CONSTEXPR
get(tuple<Ts...> && v)601   seq::type_by_index_t< I, Ts... >&& get( tuple< Ts... >&& v ) noexcept
602   {
603     using type = seq::type_by_index_t< I, Ts... >;
604     return static_cast< type&& >( static_cast< impl::tuple_value< I, type >& >( v.base ).get() );
605   }
606 
607   // get<T> helper
608   namespace impl
609   {
610     template< typename T, typename... Ts >
611     using count_of = seq::sum< std::size_t, ( std::is_same< T, Ts >::value ? 1 : 0 )... >;
612 
613     template< typename, typename, typename... >
614     struct index_of_impl;
615 
616     template< std::size_t... Is, typename T, typename... Ts >
617     struct index_of_impl< seq::index_sequence< Is... >, T, Ts... >
618       : seq::sum< std::size_t, ( std::is_same< T, Ts >::value ? Is : 0 )... >
619     {
620       static_assert( count_of< T, Ts... >::value > 0, "T not found within Ts..." );
621       static_assert( count_of< T, Ts... >::value < 2, "T must be unique within Ts..." );
622     };
623 
624     template< typename T, typename... Ts >
625     using index_of = index_of_impl< seq::index_sequence_for< Ts... >, T, Ts... >;
626   }
627 
628   // get<T>
629   template< typename T, typename... Ts >
630   TAOCPP_TUPLE_CONSTEXPR
get(const tuple<Ts...> & v)631   const T& get( const tuple< Ts... >& v ) noexcept
632   {
633     return get< impl::index_of< T, Ts... >::value >( v );
634   }
635 
636   template< typename T, typename... Ts >
637   TAOCPP_TUPLE_CONSTEXPR
get(tuple<Ts...> & v)638   T& get( tuple< Ts... >& v ) noexcept
639   {
640     return get< impl::index_of< T, Ts... >::value >( v );
641   }
642 
643   template< typename T, typename... Ts >
644   TAOCPP_TUPLE_CONSTEXPR
get(tuple<Ts...> && v)645   T&& get( tuple< Ts... >&& v ) noexcept
646   {
647     return get< impl::index_of< T, Ts... >::value >( std::move( v ) );
648   }
649 
650   // 20.4.2.7 Relational operators [tuple.rel]
651 
652   // operators helper
653   namespace impl
654   {
655     template< typename >
656     struct tuple_equal;
657 
658     template< std::size_t... Is >
659     struct tuple_equal< seq::index_sequence< Is... > >
660     {
661       template< typename T, typename U >
662       TAOCPP_TUPLE_CONSTEXPR
operator ()tao::impl::tuple_equal663       bool operator()( const T& lhs, const U& rhs ) const
664       {
665 #ifdef TAOCPP_FOLD_EXPRESSIONS
666         return ( static_cast< bool >( get< Is >( lhs ) == get< Is >( rhs ) ) && ... );
667 #else
668         bool result = true;
669         (void)swallow{ ( result = result && static_cast< bool >( get< Is >( lhs ) == get< Is >( rhs ) ) )..., true };
670         return result;
671 #endif
672       }
673     };
674 
675     template< typename >
676     struct tuple_less;
677 
678     template< std::size_t... Is >
679     struct tuple_less< seq::index_sequence< Is... > >
680     {
681       template< typename T, typename U >
682       TAOCPP_TUPLE_CONSTEXPR
operator ()tao::impl::tuple_less683       bool operator()( const T& lhs, const U& rhs ) const
684       {
685         bool result = false;
686 #ifdef TAOCPP_DUMMY // TAOCPP_FOLD_EXPRESSIONS
687         // TODO: This fold expression does not work as expected. Why?
688         (void)( ( ( result = static_cast< bool >( get< Is >( lhs ) < get< Is >( rhs ) ) ) || static_cast< bool >( get< Is >( rhs ) < get< Is >( lhs ) ) ) || ... );
689 #else
690         bool no_result_yet = false;
691         (void)swallow{ ( no_result_yet = no_result_yet || ( result = static_cast< bool >( get< Is >( lhs ) < get< Is >( rhs ) ) ) || static_cast< bool >( get< Is >( rhs ) < get< Is >( lhs ) ) )..., true };
692         (void)no_result_yet;
693 #endif
694         return result;
695       }
696     };
697   }
698 
699   // operators
700   template< typename... Ts, typename... Us, typename = impl::enable_if_t< sizeof...( Ts ) == sizeof...( Us ) > >
701   TAOCPP_TUPLE_CONSTEXPR
operator ==(const tuple<Ts...> & lhs,const tuple<Us...> & rhs)702   bool operator==( const tuple< Ts... >& lhs, const tuple< Us... >& rhs )
703   {
704     return impl::tuple_equal< seq::index_sequence_for< Ts... > >()( lhs, rhs );
705   }
706 
707   template< typename... Ts, typename... Us >
708   TAOCPP_TUPLE_CONSTEXPR
operator !=(const tuple<Ts...> & lhs,const tuple<Us...> & rhs)709   bool operator!=( const tuple< Ts... >& lhs, const tuple< Us... >& rhs )
710   {
711     return !( lhs == rhs );
712   }
713 
714   template< typename... Ts, typename... Us, typename = impl::enable_if_t< sizeof...( Ts ) == sizeof...( Us ) > >
715   TAOCPP_TUPLE_CONSTEXPR
operator <(const tuple<Ts...> & lhs,const tuple<Us...> & rhs)716   bool operator<( const tuple< Ts... >& lhs, const tuple< Us... >& rhs )
717   {
718     return impl::tuple_less< seq::index_sequence_for< Ts... > >()( lhs, rhs );
719   }
720 
721   template< typename... Ts, typename... Us >
722   TAOCPP_TUPLE_CONSTEXPR
operator >=(const tuple<Ts...> & lhs,const tuple<Us...> & rhs)723   bool operator>=( const tuple< Ts... >& lhs, const tuple< Us... >& rhs )
724   {
725     return !( lhs < rhs );
726   }
727 
728   template< typename... Ts, typename... Us >
729   TAOCPP_TUPLE_CONSTEXPR
operator >(const tuple<Ts...> & lhs,const tuple<Us...> & rhs)730   bool operator>( const tuple< Ts... >& lhs, const tuple< Us... >& rhs )
731   {
732     return rhs < lhs;
733   }
734 
735   template< typename... Ts, typename... Us >
736   TAOCPP_TUPLE_CONSTEXPR
operator <=(const tuple<Ts...> & lhs,const tuple<Us...> & rhs)737   bool operator<=( const tuple< Ts... >& lhs, const tuple< Us... >& rhs )
738   {
739     return !( rhs < lhs );
740   }
741 
742   // 20.4.2.9 Tuple specialized algorithms [tuple.special]
743 
744   // swap
745   template< typename... Ts >
swap(tuple<Ts...> & lhs,tuple<Ts...> & rhs)746   void swap( tuple< Ts... >& lhs, tuple< Ts... >& rhs )
747     noexcept( noexcept( lhs.swap( rhs ) ) )
748   {
749     lhs.swap( rhs );
750   }
751 
752   // (continued:) 20.4.2.4 Tuple creation functions [tuple.creation]
753 
754   // tuple_cat helper
755   namespace impl
756   {
757     template< std::size_t M, std::size_t... Ns >
758     struct count_less_or_equal
759       : seq::sum< std::size_t, ( ( Ns <= M ) ? 1 : 0 )... >
760     {};
761 
762     template< typename, typename >
763     struct expand;
764 
765     template< std::size_t... Is, std::size_t... Ns >
766     struct expand< seq::index_sequence< Is... >, seq::index_sequence< Ns... > >
767     {
768       using type = seq::index_sequence< count_less_or_equal< Is, Ns... >::value... >;
769     };
770 
771     template< typename I, typename S >
772     using expand_t = typename expand< I, S >::type;
773 
774     template< typename... >
775     struct tuple_cat_result;
776 
777     template< std::size_t... Os, std::size_t... Is, typename... Ts >
778     struct tuple_cat_result< seq::index_sequence< Os... >, seq::index_sequence< Is... >, Ts... >
779     {
780       using type = tuple< typename tuple_element< Is, seq::type_by_index_t< Os, Ts... > >::type... >;
781     };
782 
783     template< typename... Ts >
784     using tuple_cat_result_t = typename tuple_cat_result< Ts... >::type;
785 
786     template< typename... Ts >
787     struct tuple_cat_helper
788     {
789       using tuple_size_sequence = seq::index_sequence< tuple_size< Ts >::value... >;
790       using result_index_sequence = seq::make_index_sequence< seq::sum< tuple_size_sequence >::value >;
791 
792       using outer_index_sequence = expand_t< result_index_sequence, seq::inclusive_scan_t< tuple_size_sequence > >;
793       using inner_index_sequence = seq::minus_t< result_index_sequence, seq::map_t< outer_index_sequence, seq::exclusive_scan_t< tuple_size_sequence > > >;
794 
795       using result_type = tuple_cat_result_t< outer_index_sequence, inner_index_sequence, Ts... >;
796     };
797 
798     template< typename R, std::size_t... Os, std::size_t... Is, typename T >
799     TAOCPP_TUPLE_CONSTEXPR
tuple_cat(seq::index_sequence<Os...>,seq::index_sequence<Is...>,T v)800     R tuple_cat( seq::index_sequence< Os... >, seq::index_sequence< Is... >, T v )
801     {
802       return R( get< Is >( get< Os >( v ) )... );
803     }
804   }
805 
806   // tuple_cat
807   template< typename... Ts, typename H = impl::tuple_cat_helper< typename std::remove_reference< Ts >::type... >, typename R = typename H::result_type >
808   TAOCPP_TUPLE_CONSTEXPR
tuple_cat(Ts &&...ts)809   R tuple_cat( Ts&&... ts )
810   {
811     return impl::tuple_cat< R >( typename H::outer_index_sequence(), typename H::inner_index_sequence(), tao::forward_as_tuple( std::forward< Ts >( ts )... ) );
812   }
813 }
814 
815 #undef TAOCPP_TUPLE_CONSTEXPR
816 
817 #endif // TAOCPP_INCLUDE_TUPLE_TUPLE_HPP
818