re PR libstdc++/50159 ([C++0x] tuple_cat only accepts two arguments)
authorDaniel Krugler <daniel.kruegler@googlemail.com>
Sun, 11 Sep 2011 22:10:21 +0000 (22:10 +0000)
committerPaolo Carlini <paolo@gcc.gnu.org>
Sun, 11 Sep 2011 22:10:21 +0000 (22:10 +0000)
2011-09-11  Daniel Krugler  <daniel.kruegler@googlemail.com>

PR libstdc++/50159
* include/std/tuple (tuple_cat): Reimplement according to the
resolution of LWG 1385.
* include/std/type_traits: Define __and_ and __or_ for zero
arguments too; minor tweaks.
* testsuite/20_util/tuple/creation_functions/tuple_cat.cc: New.
* testsuite/20_util/tuple/creation_functions/constexpr.cc: Disable
for now tuple_cat test.
* testsuite/20_util/declval/requirements/1_neg.cc: Adjust dg-error
line numbers.
* testsuite/20_util/make_signed/requirements/typedefs_neg.cc: Likewise.
* testsuite/20_util/make_unsigned/requirements/typedefs_neg.cc:
Likewise.
* doc/xml/manual/status_cxx200x.xml: Update.

From-SVN: r178770

libstdc++-v3/ChangeLog
libstdc++-v3/doc/xml/manual/status_cxx200x.xml
libstdc++-v3/include/std/tuple
libstdc++-v3/include/std/type_traits
libstdc++-v3/testsuite/20_util/declval/requirements/1_neg.cc
libstdc++-v3/testsuite/20_util/make_signed/requirements/typedefs_neg.cc
libstdc++-v3/testsuite/20_util/make_unsigned/requirements/typedefs_neg.cc
libstdc++-v3/testsuite/20_util/tuple/creation_functions/constexpr.cc
libstdc++-v3/testsuite/20_util/tuple/creation_functions/tuple_cat.cc [new file with mode: 0644]

index 64f7551cc37c9518e55cb340379957bc553994e3..8edaae53b188981082bfd93a8b6afd573c674a25 100644 (file)
@@ -1,3 +1,20 @@
+2011-09-11  Daniel Krugler  <daniel.kruegler@googlemail.com>
+
+       PR libstdc++/50159
+       * include/std/tuple (tuple_cat): Reimplement according to the
+       resolution of LWG 1385.
+       * include/std/type_traits: Define __and_ and __or_ for zero
+       arguments too; minor tweaks.
+       * testsuite/20_util/tuple/creation_functions/tuple_cat.cc: New.
+       * testsuite/20_util/tuple/creation_functions/constexpr.cc: Disable
+       for now tuple_cat test.
+       * testsuite/20_util/declval/requirements/1_neg.cc: Adjust dg-error
+       line numbers.
+       * testsuite/20_util/make_signed/requirements/typedefs_neg.cc: Likewise.
+       * testsuite/20_util/make_unsigned/requirements/typedefs_neg.cc:
+       Likewise.
+       * doc/xml/manual/status_cxx200x.xml: Update.
+
 2011-09-09  Paolo Carlini  <paolo.carlini@oracle.com>
 
        * include/std/tuple: Use everywhere std::size_t... instead of int...
index 88e238f23556280c9b19c9c00fe06d2bad638cc4..3922dff83075499914e0276afe603f2341e64582 100644 (file)
@@ -443,11 +443,10 @@ particular release.
       <entry/>
     </row>
     <row>
-      <?dbhtml bgcolor="#B0B0B0" ?>
       <entry>20.4.2.4</entry>
       <entry>Tuple creation functions</entry>
-      <entry>Partial</entry>
-      <entry><code>tuple_cat</code> should be a single variadic signature (DR 1385)</entry>
+      <entry>Y</entry>
+      <entry/>
     </row>
     <row>
       <entry>20.4.2.5</entry>
index 515fb4ea001102279a8f8aded396f02b5b4f63aa..1b4a82382769787a09cd084b32028bea71c5bde2 100644 (file)
@@ -876,108 +876,184 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     forward_as_tuple(_Elements&&... __args) noexcept
     { return tuple<_Elements&&...>(std::forward<_Elements>(__args)...); }
 
-  template<std::size_t...> struct __index_holder { };    
 
-  template<std::size_t __i, typename _IdxHolder, typename... _Elements>
-    struct __index_holder_impl;
+  template<typename, std::size_t> struct array;
 
-  template<std::size_t __i, std::size_t... _Indexes, typename _IdxHolder,
-          typename... _Elements>
-    struct __index_holder_impl<__i, __index_holder<_Indexes...>,
-                              _IdxHolder, _Elements...> 
+  template<std::size_t _Int, typename _Tp, std::size_t _Nm>
+    _Tp& get(array<_Tp, _Nm>&) noexcept;
+
+  template<std::size_t _Int, typename _Tp, std::size_t _Nm>
+    _Tp&& get(array<_Tp, _Nm>&&) noexcept;
+
+  template<std::size_t _Int, typename _Tp, std::size_t _Nm>
+    const _Tp& get(const array<_Tp, _Nm>&) noexcept;
+
+  template<typename>
+    struct __is_tuple_like_impl : false_type
+    { };
+
+  template<typename... _Tps>
+    struct __is_tuple_like_impl<tuple<_Tps...>> : true_type
+    { };
+
+  template<typename _T1, typename _T2>
+    struct __is_tuple_like_impl<pair<_T1, _T2>> : true_type
+    { };
+
+  template<typename _Tp, std::size_t _Nm>
+    struct __is_tuple_like_impl<array<_Tp, _Nm>> : true_type
+    { };
+
+  // Internal type trait that allows us to sfinae-protect tuple_cat.
+  template<typename _Tp>
+    struct __is_tuple_like
+    : public __is_tuple_like_impl<typename std::remove_cv
+            <typename std::remove_reference<_Tp>::type>::type>::type
+    { };
+
+  // Stores a tuple of indices.  Also used by bind() to extract the elements
+  // in a tuple. 
+  template<std::size_t... _Indexes>
+    struct _Index_tuple
     {
-      typedef typename __index_holder_impl<__i + 1,
-                                          __index_holder<_Indexes..., __i>,
-                                          _Elements...>::type type;
+      typedef _Index_tuple<_Indexes..., sizeof...(_Indexes)> __next;
     };
-  template<std::size_t __i, std::size_t... _Indexes>
-    struct __index_holder_impl<__i, __index_holder<_Indexes...> >
-    { typedef __index_holder<_Indexes...> type; };
 
-  template<typename... _Elements>
-    struct __make_index_holder 
-    : __index_holder_impl<0, __index_holder<>, _Elements...> { };
-    
-  template<typename... _TElements, std::size_t... _TIdx,
-          typename... _UElements, std::size_t... _UIdx> 
-    inline constexpr tuple<_TElements..., _UElements...> 
-    __tuple_cat_helper(const tuple<_TElements...>& __t,
-                      const __index_holder<_TIdx...>&,
-                       const tuple<_UElements...>& __u,
-                      const __index_holder<_UIdx...>&)
-    { return tuple<_TElements..., _UElements...>(get<_TIdx>(__t)...,
-                                                get<_UIdx>(__u)...); }
-
-  template<typename... _TElements, std::size_t... _TIdx,
-          typename... _UElements, std::size_t... _UIdx> 
-    inline tuple<_TElements..., _UElements...> 
-    __tuple_cat_helper(tuple<_TElements...>&& __t,
-                      const __index_holder<_TIdx...>&, 
-                      const tuple<_UElements...>& __u,
-                      const __index_holder<_UIdx...>&)
-    { return tuple<_TElements..., _UElements...>
-       (std::forward<_TElements>(get<_TIdx>(__t))..., get<_UIdx>(__u)...); }
-
-  template<typename... _TElements, std::size_t... _TIdx,
-          typename... _UElements, std::size_t... _UIdx>
-    inline tuple<_TElements..., _UElements...> 
-    __tuple_cat_helper(const tuple<_TElements...>& __t,
-                      const __index_holder<_TIdx...>&, 
-                      tuple<_UElements...>&& __u,
-                      const __index_holder<_UIdx...>&)
-    { return tuple<_TElements..., _UElements...>
-       (get<_TIdx>(__t)..., std::forward<_UElements>(get<_UIdx>(__u))...); }
-
-  template<typename... _TElements, std::size_t... _TIdx,
-          typename... _UElements, std::size_t... _UIdx> 
-    inline tuple<_TElements..., _UElements...> 
-    __tuple_cat_helper(tuple<_TElements...>&& __t,
-                      const __index_holder<_TIdx...>&, 
-                      tuple<_UElements...>&& __u,
-                      const __index_holder<_UIdx...>&)
-    { return tuple<_TElements..., _UElements...>
-       (std::forward<_TElements>(get<_TIdx>(__t))...,
-        std::forward<_UElements>(get<_UIdx>(__u))...); }
+  // Builds an _Index_tuple<0, 1, 2, ..., _Num-1>.
+  template<std::size_t _Num>
+    struct _Build_index_tuple
+    {
+      typedef typename _Build_index_tuple<_Num - 1>::__type::__next __type;
+    };
 
-  template<typename... _TElements, typename... _UElements>
-    inline constexpr tuple<_TElements..., _UElements...> 
-    tuple_cat(const tuple<_TElements...>& __t, const tuple<_UElements...>& __u)
+  template<>
+    struct _Build_index_tuple<0>
     {
-      return __tuple_cat_helper(__t, typename
-                               __make_index_holder<_TElements...>::type(),
-                               __u, typename
-                               __make_index_holder<_UElements...>::type());
-    }
+      typedef _Index_tuple<> __type;
+    };
 
-  template<typename... _TElements, typename... _UElements>
-    inline tuple<_TElements..., _UElements...> 
-    tuple_cat(tuple<_TElements...>&& __t, const tuple<_UElements...>& __u)
+  template<std::size_t, typename, typename, std::size_t>
+    struct __make_tuple_impl;
+
+  template<std::size_t _Idx, typename _Tuple, typename... _Tp,
+           std::size_t _Nm>
+    struct __make_tuple_impl<_Idx, tuple<_Tp...>, _Tuple, _Nm>
     {
-      return __tuple_cat_helper(std::move(__t), typename
-                                __make_index_holder<_TElements...>::type(),
-                                __u, typename
-                                __make_index_holder<_UElements...>::type());
-    }
+      typedef typename __make_tuple_impl<_Idx + 1, tuple<_Tp...,
+       typename std::tuple_element<_Idx, _Tuple>::type>, _Tuple, _Nm>::__type
+      __type;
+    };
 
-  template<typename... _TElements, typename... _UElements>
-    inline tuple<_TElements..., _UElements...> 
-    tuple_cat(const tuple<_TElements...>& __t, tuple<_UElements...>&& __u)
+  template<std::size_t _Nm, typename _Tuple, typename... _Tp>
+    struct __make_tuple_impl<_Nm, tuple<_Tp...>, _Tuple, _Nm>
     {
-      return __tuple_cat_helper(__t, typename
-                               __make_index_holder<_TElements...>::type(),
-                               std::move(__u), typename
-                               __make_index_holder<_UElements...>::type());
-    }
+      typedef tuple<_Tp...> __type;
+    };
 
-  template<typename... _TElements, typename... _UElements>
-    inline tuple<_TElements..., _UElements...>
-    tuple_cat(tuple<_TElements...>&& __t, tuple<_UElements...>&& __u)
+  template<typename _Tuple>
+    struct __do_make_tuple
+    : public __make_tuple_impl<0, tuple<>, _Tuple,
+                               std::tuple_size<_Tuple>::value>
+    { };
+
+  // Returns the std::tuple equivalent of a tuple-like type.
+  template<typename _Tuple>
+    struct __make_tuple
+    : public __do_make_tuple<typename std::remove_cv
+            <typename std::remove_reference<_Tuple>::type>::type>
+    { };
+
+  // Combines several std::tuple's into a single one.
+  template<typename...>
+    struct __combine_tuples;
+
+  template<>
+    struct __combine_tuples<>
+    {
+      typedef tuple<> __type;
+    };
+
+  template<typename... _Ts>
+    struct __combine_tuples<tuple<_Ts...>>
     {
-      return __tuple_cat_helper(std::move(__t), typename
-                               __make_index_holder<_TElements...>::type(),
-                               std::move(__u), typename
-                               __make_index_holder<_UElements...>::type());
+      typedef tuple<_Ts...> __type;
+    };
+
+  template<typename... _T1s, typename... _T2s, typename... _Rem>
+    struct __combine_tuples<tuple<_T1s...>, tuple<_T2s...>, _Rem...>
+    {
+      typedef typename __combine_tuples<tuple<_T1s..., _T2s...>,
+                                       _Rem...>::__type __type;
+    };
+
+  // Computes the result type of tuple_cat given a set of tuple-like types.
+  template<typename... _Tpls>
+    struct __tuple_cat_result
+    {
+      typedef typename __combine_tuples
+        <typename __make_tuple<_Tpls>::__type...>::__type __type;
+    };
+
+  // Helper to determine the index set for the first tuple-like
+  // type of a given set.
+  template<typename...>
+    struct __make_1st_indices;
+
+  template<>
+    struct __make_1st_indices<>
+    {
+      typedef std::_Index_tuple<> __type;
+    };
+
+  template<typename _Tp, typename... _Tpls>
+    struct __make_1st_indices<_Tp, _Tpls...>
+    {
+      typedef typename std::_Build_index_tuple<std::tuple_size<
+       typename std::remove_reference<_Tp>::type>::value>::__type __type;
+    };
+
+  // Performs the actual concatenation by step-wise expanding tuple-like
+  // objects into the elements,  which are finally forwarded into the
+  // result tuple.
+  template<typename _Ret, typename _Indices, typename... _Tpls>
+    struct __tuple_concater;
+
+  template<typename _Ret, std::size_t... _Is, typename _Tp, typename... _Tpls>
+    struct __tuple_concater<_Ret, std::_Index_tuple<_Is...>, _Tp, _Tpls...>
+    {
+      template<typename... _Us>
+        static _Ret
+        _S_do(_Tp&& __tp, _Tpls&&... __tps, _Us&&... __us)
+        {
+         typedef typename __make_1st_indices<_Tpls...>::__type __idx;
+         typedef __tuple_concater<_Ret, __idx, _Tpls...>      __next;
+         return __next::_S_do(std::forward<_Tpls>(__tps)...,
+                              std::forward<_Us>(__us)...,
+                              std::get<_Is>(std::forward<_Tp>(__tp))...);
+       }
+    };
+
+  template<typename _Ret>
+    struct __tuple_concater<_Ret, std::_Index_tuple<>>
+    {
+      template<typename... _Us>
+       static _Ret
+       _S_do(_Us&&... __us)
+        {
+         return _Ret(std::forward<_Us>(__us)...);
+       }
+    };
+
+  template<typename... _Tpls>
+    inline typename
+    std::enable_if<__and_<__is_tuple_like<_Tpls>...>::value,
+                   typename __tuple_cat_result<_Tpls...>::__type>::type
+    tuple_cat(_Tpls&&... __tpls)
+    {
+      typedef typename __tuple_cat_result<_Tpls...>::__type __ret;
+      typedef typename __make_1st_indices<_Tpls...>::__type __idx;
+      typedef __tuple_concater<__ret, __idx, _Tpls...> __concater;
+      return __concater::_S_do(std::forward<_Tpls>(__tpls)...);
     }
 
   template<typename... _Elements>
@@ -1007,29 +1083,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename... _Types, typename _Alloc>
     struct uses_allocator<tuple<_Types...>, _Alloc> : true_type { };
 
-  /**
-   * Stores a tuple of indices. Used by bind() to extract the elements
-   * in a tuple. 
-   */
-  template<std::size_t... _Indexes>
-    struct _Index_tuple
-    {
-      typedef _Index_tuple<_Indexes..., sizeof...(_Indexes)> __next;
-    };
-
-  /// Builds an _Index_tuple<0, 1, 2, ..., _Num-1>.
-  template<std::size_t _Num>
-    struct _Build_index_tuple
-    {
-      typedef typename _Build_index_tuple<_Num-1>::__type::__next __type;
-    };
-
-  template<>
-    struct _Build_index_tuple<0>
-    {
-      typedef _Index_tuple<> __type;
-    };
-
   // See stl_pair.h...
   template<class _T1, class _T2>
     template<typename _Tp, typename... _Args>
index 9e4070215059577d5c0fab9570831e2a268fb768..155f7dfe6b9d9d0aff74f3cbe5bc0b683401489e 100644 (file)
@@ -45,23 +45,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
    * @addtogroup metaprogramming
    * @{
    */
-  struct __sfinae_types
-  {
-    typedef char __one;
-    typedef struct { char __arr[2]; } __two;
-  };
+
+  /// integral_constant
+  template<typename _Tp, _Tp __v>
+    struct integral_constant
+    {
+      static constexpr _Tp                  value = __v;
+      typedef _Tp                           value_type;
+      typedef integral_constant<_Tp, __v>   type;
+      constexpr operator value_type() { return value; }
+    };
+  
+  /// typedef for true_type
+  typedef integral_constant<bool, true>     true_type;
+
+  /// typedef for false_type
+  typedef integral_constant<bool, false>    false_type;
+
+  template<typename _Tp, _Tp __v>
+    constexpr _Tp integral_constant<_Tp, __v>::value;
 
   // Meta programming helper types.
 
   template<bool, typename, typename>
     struct conditional;
 
-  template<typename _Tp, _Tp>
-    struct integral_constant;
-
   template<typename...>
     struct __or_;
 
+  template<>
+    struct __or_<>
+    : public false_type
+    { };
+
   template<typename _B1>
     struct __or_<_B1>
     : public _B1
@@ -80,6 +96,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename...>
     struct __and_;
 
+  template<>
+    struct __and_<>
+    : public true_type
+    { };
+
   template<typename _B1>
     struct __and_<_B1>
     : public _B1
@@ -100,26 +121,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     : public integral_constant<bool, !_Pp::value>
     { };
 
-  // helper class.
-
-  /// integral_constant
-  template<typename _Tp, _Tp __v>
-    struct integral_constant
-    {
-      static constexpr _Tp                  value = __v;
-      typedef _Tp                           value_type;
-      typedef integral_constant<_Tp, __v>   type;
-      constexpr operator value_type() { return value; }
-    };
-  
-  /// typedef for true_type
-  typedef integral_constant<bool, true>     true_type;
-
-  /// typedef for false_type
-  typedef integral_constant<bool, false>    false_type;
-
-  template<typename _Tp, _Tp __v>
-    constexpr _Tp integral_constant<_Tp, __v>::value;
+  struct __sfinae_types
+  {
+    typedef char __one;
+    typedef struct { char __arr[2]; } __two;
+  };
 
   // primary type categories.
 
index a5f4b286bc0a2d6c0511254448d5d375f5af4f99..195b9c291739c672d3d5d3a4051ebf9363099637 100644 (file)
@@ -19,7 +19,7 @@
 // with this library; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
-// { dg-error "static assertion failed" "" { target *-*-* } 1725 }
+// { dg-error "static assertion failed" "" { target *-*-* } 1731 }
 
 #include <utility>
 
index f37d4fbf88a182cf10d9ae5ba3932d7f1a74364a..b3266580f5fe9dfa054264f41b02c29fd5cb105c 100644 (file)
@@ -48,5 +48,5 @@ void test01()
 // { dg-error "required from here" "" { target *-*-* } 40 }
 // { dg-error "required from here" "" { target *-*-* } 42 }
 
-// { dg-error "invalid use of incomplete type" "" { target *-*-* } 1511 }
-// { dg-error "declaration of" "" { target *-*-* } 1475 }
+// { dg-error "invalid use of incomplete type" "" { target *-*-* } 1517 }
+// { dg-error "declaration of" "" { target *-*-* } 1481 }
index 497f170d97aa641fc779ebb05973f22847e0f40b..3de08b30aa3620ac6f829b3cbf7f8669e28167ca 100644 (file)
@@ -48,5 +48,5 @@ void test01()
 // { dg-error "required from here" "" { target *-*-* } 40 }
 // { dg-error "required from here" "" { target *-*-* } 42 }
 
-// { dg-error "invalid use of incomplete type" "" { target *-*-* } 1435 }
-// { dg-error "declaration of" "" { target *-*-* } 1399 }
+// { dg-error "invalid use of incomplete type" "" { target *-*-* } 1441 }
+// { dg-error "declaration of" "" { target *-*-* } 1405 }
index a5b5441d4a794a80715b29ec8ffa327d0b03512e..82bbe1f104bd7badd7ac01148b374be50c677b60 100644 (file)
@@ -63,6 +63,7 @@ test_get()
 }
 
 // tuple_cat
+#if 0
 void
 test_tuple_cat()
 {
@@ -73,7 +74,7 @@ test_tuple_cat()
   constexpr tuple_type2 t2 { 55, 99, 77.77 };
   constexpr auto cat1 = std::tuple_cat(t1, t2);
 }
-
+#endif
 
 int
 main()
@@ -84,7 +85,9 @@ main()
 
   test_get();
 
+#if 0
   test_tuple_cat();
+#endif
 
   return 0;
 }
diff --git a/libstdc++-v3/testsuite/20_util/tuple/creation_functions/tuple_cat.cc b/libstdc++-v3/testsuite/20_util/tuple/creation_functions/tuple_cat.cc
new file mode 100644 (file)
index 0000000..b2279a1
--- /dev/null
@@ -0,0 +1,131 @@
+// { dg-options "-std=gnu++0x" }
+
+// Copyright (C) 2011 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// Tuple
+
+#include <tuple>
+#include <array>
+
+static_assert(std::is_same<decltype(std::tuple_cat()),
+             std::tuple<>>::value, "Error");
+static_assert(std::is_same<decltype(std::tuple_cat
+                                   (std::declval<std::tuple<>>())),
+              std::tuple<>>::value, "Error");
+static_assert(std::is_same<decltype(std::tuple_cat
+                                   (std::declval<std::tuple<>&>())),
+              std::tuple<>>::value, "Error");
+static_assert(std::is_same<decltype(std::tuple_cat
+                                   (std::declval<const std::tuple<>>())),
+              std::tuple<>>::value, "Error");
+static_assert(std::is_same<decltype(std::tuple_cat
+                                   (std::declval<const std::tuple<>&>())),
+              std::tuple<>>::value, "Error");
+static_assert(std::is_same<decltype(std::tuple_cat
+                                   (std::declval<std::pair<int, bool>>())),
+              std::tuple<int, bool>>::value, "Error");
+static_assert(std::is_same<decltype(std::tuple_cat
+                                   (std::declval<std::pair<int, bool>&>())),
+              std::tuple<int, bool>>::value, "Error");
+static_assert(std::is_same<decltype
+             (std::tuple_cat(std::declval<const std::pair<int, bool>>())),
+              std::tuple<int, bool>>::value, "Error");
+static_assert(std::is_same<decltype
+             (std::tuple_cat(std::declval<const std::pair<int, bool>&>())),
+              std::tuple<int, bool>>::value, "Error");
+static_assert(std::is_same<decltype
+             (std::tuple_cat(std::declval<std::array<int, 3>>())),
+              std::tuple<int, int, int>>::value, "Error");
+static_assert(std::is_same<decltype
+             (std::tuple_cat(std::declval<std::array<int, 3>&>())),
+              std::tuple<int, int, int>>::value, "Error");
+static_assert(std::is_same<decltype
+             (std::tuple_cat(std::declval<const std::array<int, 3>>())),
+              std::tuple<int, int, int>>::value, "Error");
+static_assert(std::is_same<decltype
+             (std::tuple_cat(std::declval<const std::array<int, 3>&>())),
+              std::tuple<int, int, int>>::value, "Error");
+static_assert(std::is_same<decltype
+             (std::tuple_cat
+              (std::declval<std::tuple<>>(), std::declval<std::tuple<>>())),
+              std::tuple<>>::value, "Error");
+static_assert(std::is_same<decltype
+             (std::tuple_cat
+              (std::declval<std::tuple<>>(), std::declval<std::tuple<>>(),
+               std::declval<std::tuple<>>())), std::tuple<>>::value, "Error");
+static_assert(std::is_same<decltype
+             (std::tuple_cat
+              (std::declval<std::tuple<>>(),
+               std::declval<std::array<char, 0>>(),
+               std::declval<std::array<int, 0>>(),
+               std::declval<std::tuple<>>())), std::tuple<>>::value, "Error");
+static_assert(std::is_same<decltype
+             (std::tuple_cat
+              (std::declval<std::tuple<int>>(),
+               std::declval<std::tuple<double>>())),
+              std::tuple<int, double>>::value, "Error");
+static_assert(std::is_same<decltype
+             (std::tuple_cat
+              (std::declval<std::tuple<int>>(),
+               std::declval<std::tuple<double>>(),
+               std::declval<std::tuple<const long&>>())),
+              std::tuple<int, double, const long&>>::value, "Error");
+static_assert(std::is_same<decltype
+             (std::tuple_cat
+              (std::declval<std::array<wchar_t, 3>&>(),
+               std::declval<std::tuple<double>>(),
+               std::declval<std::tuple<>>(),
+               std::declval<std::tuple<unsigned&>>(),
+               std::declval<std::pair<bool, std::nullptr_t>>())),
+              std::tuple<wchar_t, wchar_t, wchar_t,
+             double, unsigned&, bool, std::nullptr_t>
+              >::value, "Error");
+
+int main()
+{
+  std::tuple_cat();
+  std::tuple_cat(std::tuple<>{ });
+  std::tuple_cat(std::tuple<>{ }, std::tuple<>{ });
+  std::array<int, 3> a3;
+  std::tuple_cat(a3);
+  std::pair<double, bool> pdb;
+  std::tuple<unsigned, float, std::nullptr_t, void*> t;
+  int i{ };
+  double d{ };
+  int* pi{ };
+  std::tuple<int&, double&, int*&> to{i, d, pi};
+  std::tuple_cat(pdb);
+  std::tuple_cat(to);
+  std::tuple_cat(to, to);
+  std::tuple_cat(a3, pdb);
+  std::tuple_cat(a3, pdb, t);
+  std::tuple_cat(a3, pdb, t, a3);
+  std::tuple_cat(a3, pdb, t, a3, pdb, t);
+
+  static_assert(std::is_same<decltype
+               (std::tuple_cat(a3, pdb, t, a3, pdb, t)),
+                std::tuple<int, int, int, double, bool,
+               unsigned, float, std::nullptr_t, void*,
+               int, int, int, double, bool, unsigned,
+               float, std::nullptr_t, void*>
+                >::value, "Error");
+
+  std::tuple_cat(std::tuple<int, char, void*>{}, to, a3,
+                std::tuple<>{}, std::pair<float,
+                std::nullptr_t>{}, pdb, to);
+}