diff mbox series

RFC: remove std::tuple<T, U> partial specialization

Message ID 20180817192913.GQ13845@redhat.com
State New
Headers show
Series RFC: remove std::tuple<T, U> partial specialization | expand

Commit Message

Jonathan Wakely Aug. 17, 2018, 7:29 p.m. UTC
While fixing PR 86963 I realised we can get rid of the 2-tuple partial
specialization, and just add the relevant constructors and assignment
operators to the primary template. They're all constrained anyway, so
they won't be available except when sizeof...(_Elements) == 2.

This patch also removes the recursive evaluation of exception
specifications on the assignment operators and tuple::swap members,
just defining them on std::tuple without depending on the base
classes.

BUT it causes:

FAIL: 20_util/tuple/cons/allocator_with_any.cc execution test
/home/jwakely/src/gcc/gcc/libstdc++-v3/testsuite/20_util/tuple/cons/allocator_with_any.cc:35: void test01(): Assertion 'std::get<0>(t).empty()' failed.

where that test does:

    std::tuple<any, any> t(std::allocator_arg,
			   std::allocator<any>{});
    VERIFY(std::get<0>(t).empty());
    VERIFY(std::get<1>(t).empty());

That's because the partial specialization had a special case for
allocator_arg_t:

-      template<typename _U1, typename _U2, typename
-        enable_if<_TMC::template
-                    _MoveConstructibleTuple<_U1, _U2>()
-                  && _TMC::template
-                    _ImplicitlyMoveConvertibleTuple<_U1, _U2>()
-                 && !is_same<typename decay<_U1>::type,
-                             allocator_arg_t>::value,
-       bool>::type = true>
-        constexpr tuple(_U1&& __a1, _U2&& __a2)
-       : _Inherited(std::forward<_U1>(__a1), std::forward<_U2>(__a2)) { }

That was added by https://gcc.gnu.org/ml/libstdc++/2016-12/msg00122.html
but I see no justification for that in the standard (and neither
libc++ nor MSFTL does anything special here, so they fail the test
too).

Ville, I'm no longer convinced by your rationale. Why is that
constraint on the 2-tuple partial specialization when the standard
doesn't say anything like "if sizeof...(Types)==2 this constructor
shall not participate in overload resolution unless decay_t<U_i> is
not allocator_arg_t" for the tuple(_UTypes&&...) constructor?

As far as I can tell, the standard says that the test is wrong.

If we think the test is right, we should report a defect. Either way,
I think this patch would be a nice simplification. We can either fix
(or just remove) the test, or constrain the primary template.

Comments

Ville Voutilainen Aug. 17, 2018, 7:46 p.m. UTC | #1
On 17 August 2018 at 22:29, Jonathan Wakely <jwakely@redhat.com> wrote:
> That was added by https://gcc.gnu.org/ml/libstdc++/2016-12/msg00122.html
> but I see no justification for that in the standard (and neither
> libc++ nor MSFTL does anything special here, so they fail the test
> too).
>
> Ville, I'm no longer convinced by your rationale. Why is that

You mean the part where I said "the variadic constructor doesn't run
into this problem"?
I may have been wrong about the variadic.

> constraint on the 2-tuple partial specialization when the standard
> doesn't say anything like "if sizeof...(Types)==2 this constructor
> shall not participate in overload resolution unless decay_t<U_i> is
> not allocator_arg_t" for the tuple(_UTypes&&...) constructor?
>
> As far as I can tell, the standard says that the test is wrong.
>
> If we think the test is right, we should report a defect. Either way,
> I think this patch would be a nice simplification. We can either fix
> (or just remove) the test, or constrain the primary template.

I think the test is reasonable to the point of being obvious. If you
pass an allocator_arg
and an allocator, the intent is that uses-allocator construction is
always used, not that
it's used unless the element type is constructible from allocator_arg
and an allocator.
While the explicitness and presence of some tuple constructors depends
on the properties
of the element types, the semantics of the allocator constructors
should not depend on them
to decide whether to do uses-allocator construction or not - that
would be a vector<bool> again.
Ville Voutilainen Aug. 17, 2018, 7:47 p.m. UTC | #2
On 17 August 2018 at 22:46, Ville Voutilainen
<ville.voutilainen@gmail.com> wrote:
>>
>> If we think the test is right, we should report a defect. Either way,
>> I think this patch would be a nice simplification. We can either fix
>> (or just remove) the test, or constrain the primary template.
>
> I think the test is reasonable to the point of being obvious. If you
> pass an allocator_arg
> and an allocator, the intent is that uses-allocator construction is
> always used, not that
> it's used unless the element type is constructible from allocator_arg
> and an allocator.
> While the explicitness and presence of some tuple constructors depends
> on the properties
> of the element types, the semantics of the allocator constructors
> should not depend on them
> to decide whether to do uses-allocator construction or not - that
> would be a vector<bool> again.

So yes, I strongly think we should report a defect.
Jonathan Wakely Aug. 17, 2018, 7:55 p.m. UTC | #3
On 17/08/18 22:46 +0300, Ville Voutilainen wrote:
>On 17 August 2018 at 22:29, Jonathan Wakely <jwakely@redhat.com> wrote:
>> That was added by https://gcc.gnu.org/ml/libstdc++/2016-12/msg00122.html
>> but I see no justification for that in the standard (and neither
>> libc++ nor MSFTL does anything special here, so they fail the test
>> too).
>>
>> Ville, I'm no longer convinced by your rationale. Why is that
>
>You mean the part where I said "the variadic constructor doesn't run
>into this problem"?

Yes. Either we shouldn't constrain our 2-tuple partial specialization,
or the standard should require it for the sizeof...(Types)==2 case.

>I may have been wrong about the variadic.

On 17/08/18 22:47 +0300, Ville Voutilainen wrote:
>So yes, I strongly think we should report a defect.

OK, I'll do that.
diff mbox series

Patch

commit 6fea64cd9f546e587c7212c7b95fde0b3005f936
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Fri Aug 17 19:07:36 2018 +0100

    Remove std::tuple<T, U> partial specialization

diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
index 955b853066f..7cf3184b4aa 100644
--- a/libstdc++-v3/include/std/tuple
+++ b/libstdc++-v3/include/std/tuple
@@ -298,8 +298,6 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       _Tuple_impl&
       operator=(_Tuple_impl&& __in)
-      noexcept(__and_<is_nothrow_move_assignable<_Head>,
-	              is_nothrow_move_assignable<_Inherited>>::value)
       {
 	_M_head(*this) = std::forward<_Head>(_M_head(__in));
 	_M_tail(*this) = std::move(_M_tail(__in));
@@ -329,8 +327,6 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     protected:
       void
       _M_swap(_Tuple_impl& __in)
-      noexcept(__is_nothrow_swappable<_Head>::value
-               && noexcept(_M_tail(__in)._M_swap(_M_tail(__in))))
       {
 	using std::swap;
 	swap(_M_head(*this), _M_head(__in));
@@ -429,7 +425,6 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       _Tuple_impl&
       operator=(_Tuple_impl&& __in)
-      noexcept(is_nothrow_move_assignable<_Head>::value)
       {
 	_M_head(*this) = std::forward<_Head>(_M_head(__in));
 	return *this;
@@ -455,7 +450,6 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     protected:
       void
       _M_swap(_Tuple_impl& __in)
-      noexcept(__is_nothrow_swappable<_Head>::value)
       {
 	using std::swap;
 	swap(_M_head(*this), _M_head(__in));
@@ -502,6 +496,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
                      __not_<is_constructible<_Elements..., _SrcTuple>>
               >::value;
     }
+
     template<typename... _UElements>
     static constexpr bool _NotSameTuple()
     {
@@ -544,6 +539,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     {
       return true;
     }
+
     template<typename... _UElements>
     static constexpr bool _NotSameTuple()
     {
@@ -740,6 +736,39 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
         explicit constexpr tuple(tuple<_UElements...>&& __in)
         : _Inherited(static_cast<_Tuple_impl<0, _UElements...>&&>(__in)) { }
 
+      // Shortcut for constraining the constructors taking pairs.
+      using _TP = _TC<sizeof...(_Elements) == 2, _Elements...>;
+
+      template<typename _U1, typename _U2,
+	  __enable_if_t<_TP::template _ConstructibleTuple<_U1, _U2>()
+	    && _TP::template _ImplicitlyConvertibleTuple<_U1, _U2>(),
+	  bool> = true>
+	constexpr tuple(const pair<_U1, _U2>& __in)
+	: _Inherited(__in.first, __in.second) { }
+
+      template<typename _U1, typename _U2,
+        __enable_if_t<_TP::template _ConstructibleTuple<_U1, _U2>()
+	    && ! _TP::template _ImplicitlyConvertibleTuple<_U1, _U2>(),
+	  bool> = false>
+	explicit constexpr tuple(const pair<_U1, _U2>& __in)
+	: _Inherited(__in.first, __in.second) { }
+
+      template<typename _U1, typename _U2,
+	__enable_if_t<_TP::template _MoveConstructibleTuple<_U1, _U2>()
+	    && _TP::template _ImplicitlyMoveConvertibleTuple<_U1, _U2>(),
+	  bool> = true>
+	constexpr tuple(pair<_U1, _U2>&& __in)
+	: _Inherited(std::forward<_U1>(__in.first),
+		     std::forward<_U2>(__in.second)) { }
+
+      template<typename _U1, typename _U2,
+        __enable_if_t<_TP::template _MoveConstructibleTuple<_U1, _U2>()
+	    && ! _TP::template _ImplicitlyMoveConvertibleTuple<_U1, _U2>(),
+	  bool> = false>
+	explicit constexpr tuple(pair<_U1, _U2>&& __in)
+	: _Inherited(std::forward<_U1>(__in.first),
+		     std::forward<_U2>(__in.second)) { }
+
       // Allocator-extended constructors.
 
       template<typename _Alloc>
@@ -764,8 +793,9 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
                  && !_TCC<_Dummy>::template
                    _ImplicitlyConvertibleTuple<_Elements...>(),
                bool>::type=false>
-	explicit tuple(allocator_arg_t __tag, const _Alloc& __a,
-                       const _Elements&... __elements)
+	explicit
+	tuple(allocator_arg_t __tag, const _Alloc& __a,
+              const _Elements&... __elements)
 	: _Inherited(__tag, __a, __elements...) { }
 
       template<typename _Alloc, typename... _UElements, typename
@@ -785,7 +815,8 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
                   && !_TMC<_UElements...>::template
                     _ImplicitlyMoveConvertibleTuple<_UElements...>(),
         bool>::type=false>
-	explicit tuple(allocator_arg_t __tag, const _Alloc& __a,
+	explicit
+	tuple(allocator_arg_t __tag, const _Alloc& __a,
 	      _UElements&&... __elements)
 	: _Inherited(__tag, __a, std::forward<_UElements>(__elements)...)
         { }
@@ -822,7 +853,8 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
                   && _TNTC<_Dummy>::template
                     _NonNestedTuple<tuple<_UElements...>&&>(),
         bool>::type=false>
-	explicit tuple(allocator_arg_t __tag, const _Alloc& __a,
+	explicit
+	tuple(allocator_arg_t __tag, const _Alloc& __a,
 	      const tuple<_UElements...>& __in)
 	: _Inherited(__tag, __a,
 	             static_cast<const _Tuple_impl<0, _UElements...>&>(__in))
@@ -852,12 +884,53 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
                   && _TNTC<_Dummy>::template
                     _NonNestedTuple<tuple<_UElements...>&&>(),
         bool>::type=false>
-	explicit tuple(allocator_arg_t __tag, const _Alloc& __a,
+	explicit
+	tuple(allocator_arg_t __tag, const _Alloc& __a,
 	      tuple<_UElements...>&& __in)
 	: _Inherited(__tag, __a,
 	             static_cast<_Tuple_impl<0, _UElements...>&&>(__in))
 	{ }
 
+      template<typename _Alloc, typename _U1, typename _U2,
+	  __enable_if_t<_TP::template _ConstructibleTuple<_U1, _U2>()
+	    && _TP::template _ImplicitlyConvertibleTuple<_U1, _U2>(),
+	  bool> = true>
+        constexpr
+	tuple(allocator_arg_t __tag, const _Alloc& __a,
+	      const pair<_U1, _U2>& __in)
+	: _Inherited(__tag, __a, __in.first, __in.second)
+	{ }
+
+      template<typename _Alloc, typename _U1, typename _U2,
+        __enable_if_t<_TP::template _ConstructibleTuple<_U1, _U2>()
+	    && ! _TP::template _ImplicitlyConvertibleTuple<_U1, _U2>(),
+	  bool> = false>
+        explicit constexpr
+	tuple(allocator_arg_t __tag, const _Alloc& __a,
+	      const pair<_U1, _U2>& __in)
+	: _Inherited(__tag, __a, __in.first, __in.second)
+	{ }
+
+      template<typename _Alloc, typename _U1, typename _U2,
+	__enable_if_t<_TP::template _MoveConstructibleTuple<_U1, _U2>()
+	    && _TP::template _ImplicitlyMoveConvertibleTuple<_U1, _U2>(),
+	  bool> = true>
+        constexpr
+	tuple(allocator_arg_t __tag, const _Alloc& __a, pair<_U1, _U2>&& __in)
+	: _Inherited(__tag, __a, std::forward<_U1>(__in.first),
+		     std::forward<_U2>(__in.second))
+	{ }
+
+      template<typename _Alloc, typename _U1, typename _U2,
+        __enable_if_t<_TP::template _MoveConstructibleTuple<_U1, _U2>()
+	    && ! _TP::template _ImplicitlyMoveConvertibleTuple<_U1, _U2>(),
+	  bool> = false>
+        explicit constexpr
+	tuple(allocator_arg_t __tag, const _Alloc& __a, pair<_U1, _U2>&& __in)
+	: _Inherited(__tag, __a, std::forward<_U1>(__in.first),
+		     std::forward<_U2>(__in.second))
+	{ }
+
       tuple&
       operator=(typename conditional<__assignable<const _Elements&...>(),
 				     const tuple&,
@@ -896,9 +969,29 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  return *this;
 	}
 
+      template<typename _U1, typename _U2>
+	__enable_if_t<__assignable<const _U1&, const _U2&>(), tuple&>
+	operator=(const pair<_U1, _U2>& __in)
+	noexcept(__nothrow_assignable<const _U1&, const _U2&>())
+	{
+	  this->_M_head(*this) = __in.first;
+	  this->_M_tail(*this)._M_head(*this) = __in.second;
+	  return *this;
+	}
+
+      template<typename _U1, typename _U2>
+	__enable_if_t<__assignable<_U1, _U2>(), tuple&>
+	operator=(pair<_U1, _U2>&& __in)
+	noexcept(__nothrow_assignable<_U1, _U2>())
+	{
+	  this->_M_head(*this) = std::forward<_U1>(__in.first);
+	  this->_M_tail(*this)._M_head(*this) = std::forward<_U2>(__in.second);
+	  return *this;
+	}
+
       void
       swap(tuple& __in)
-      noexcept(noexcept(__in._M_swap(__in)))
+      noexcept(__and_<__is_nothrow_swappable<_Elements>...>::value)
       { _Inherited::_M_swap(__in); }
     };
 
@@ -931,389 +1024,6 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	tuple(allocator_arg_t, const _Alloc&, const tuple&) { }
     };
 
-  /// Partial specialization, 2-element tuple.
-  /// Includes construction and assignment from a pair.
-  template<typename _T1, typename _T2>
-    class tuple<_T1, _T2>
-    : public _Tuple_impl<0, _T1, _T2>,
-      private __tuple_base<tuple<_T1, _T2>>
-    {
-      typedef _Tuple_impl<0, _T1, _T2> _Inherited;
-
-      template<typename _U1, typename _U2>
-	static constexpr bool __assignable()
-	{
-	  return __and_<is_assignable<_T1&, _U1>,
-			is_assignable<_T2&, _U2>>::value;
-	}
-
-      template<typename _U1, typename _U2>
-	static constexpr bool __nothrow_assignable()
-	{
-	  return __and_<is_nothrow_assignable<_T1&, _U1>,
-			is_nothrow_assignable<_T2&, _U2>>::value;
-	}
-
-    public:
-      template <typename _U1 = _T1,
-                typename _U2 = _T2,
-                typename enable_if<__and_<
-                                     __is_implicitly_default_constructible<_U1>,
-                                     __is_implicitly_default_constructible<_U2>>
-                                   ::value, bool>::type = true>
-	constexpr tuple()
-	: _Inherited() { }
-
-      template <typename _U1 = _T1,
-                typename _U2 = _T2,
-                typename enable_if<
-                  __and_<
-                    is_default_constructible<_U1>,
-                    is_default_constructible<_U2>,
-                    __not_<
-                      __and_<__is_implicitly_default_constructible<_U1>,
-                             __is_implicitly_default_constructible<_U2>>>>
-                  ::value, bool>::type = false>
-	explicit constexpr tuple()
-	: _Inherited() { }
-
-      // Shortcut for the cases where constructors taking _T1, _T2
-      // need to be constrained.
-      template<typename _Dummy> using _TCC =
-        _TC<is_same<_Dummy, void>::value, _T1, _T2>;
-
-      template<typename _Dummy = void, typename
-               enable_if<_TCC<_Dummy>::template
-                           _ConstructibleTuple<_T1, _T2>()
-                         && _TCC<_Dummy>::template
-                           _ImplicitlyConvertibleTuple<_T1, _T2>(),
-	bool>::type = true>
-        constexpr tuple(const _T1& __a1, const _T2& __a2)
-        : _Inherited(__a1, __a2) { }
-
-      template<typename _Dummy = void, typename
-               enable_if<_TCC<_Dummy>::template
-                           _ConstructibleTuple<_T1, _T2>()
-                         && !_TCC<_Dummy>::template
-                           _ImplicitlyConvertibleTuple<_T1, _T2>(),
-	bool>::type = false>
-        explicit constexpr tuple(const _T1& __a1, const _T2& __a2)
-        : _Inherited(__a1, __a2) { }
-
-      // Shortcut for the cases where constructors taking _U1, _U2
-      // need to be constrained.
-      using _TMC = _TC<true, _T1, _T2>;
-
-      template<typename _U1, typename _U2, typename
-        enable_if<_TMC::template
-                    _MoveConstructibleTuple<_U1, _U2>()
-                  && _TMC::template
-                    _ImplicitlyMoveConvertibleTuple<_U1, _U2>()
-	          && !is_same<typename decay<_U1>::type,
-			      allocator_arg_t>::value,
-	bool>::type = true>
-        constexpr tuple(_U1&& __a1, _U2&& __a2)
-	: _Inherited(std::forward<_U1>(__a1), std::forward<_U2>(__a2)) { }
-
-      template<typename _U1, typename _U2, typename
-        enable_if<_TMC::template
-                    _MoveConstructibleTuple<_U1, _U2>()
-                  && !_TMC::template
-                    _ImplicitlyMoveConvertibleTuple<_U1, _U2>()
-	          && !is_same<typename decay<_U1>::type,
-			      allocator_arg_t>::value,
-	bool>::type = false>
-        explicit constexpr tuple(_U1&& __a1, _U2&& __a2)
-	: _Inherited(std::forward<_U1>(__a1), std::forward<_U2>(__a2)) { }
-
-      constexpr tuple(const tuple&) = default;
-
-      constexpr tuple(tuple&&) = default;
-
-      template<typename _U1, typename _U2, typename
-        enable_if<_TMC::template
-                    _ConstructibleTuple<_U1, _U2>()
-                  && _TMC::template
-                    _ImplicitlyConvertibleTuple<_U1, _U2>(),
-	bool>::type = true>
-        constexpr tuple(const tuple<_U1, _U2>& __in)
-	: _Inherited(static_cast<const _Tuple_impl<0, _U1, _U2>&>(__in)) { }
-
-      template<typename _U1, typename _U2, typename
-        enable_if<_TMC::template
-                    _ConstructibleTuple<_U1, _U2>()
-                  && !_TMC::template
-                    _ImplicitlyConvertibleTuple<_U1, _U2>(),
-	bool>::type = false>
-        explicit constexpr tuple(const tuple<_U1, _U2>& __in)
-	: _Inherited(static_cast<const _Tuple_impl<0, _U1, _U2>&>(__in)) { }
-
-      template<typename _U1, typename _U2, typename
-        enable_if<_TMC::template
-                    _MoveConstructibleTuple<_U1, _U2>()
-                  && _TMC::template
-                    _ImplicitlyMoveConvertibleTuple<_U1, _U2>(),
-	bool>::type = true>
-        constexpr tuple(tuple<_U1, _U2>&& __in)
-	: _Inherited(static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in)) { }
-
-      template<typename _U1, typename _U2, typename
-        enable_if<_TMC::template
-                    _MoveConstructibleTuple<_U1, _U2>()
-                  && !_TMC::template
-                    _ImplicitlyMoveConvertibleTuple<_U1, _U2>(),
-	bool>::type = false>
-        explicit constexpr tuple(tuple<_U1, _U2>&& __in)
-	: _Inherited(static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in)) { }
-
-      template<typename _U1, typename _U2, typename
-        enable_if<_TMC::template
-                    _ConstructibleTuple<_U1, _U2>()
-                  && _TMC::template
-                    _ImplicitlyConvertibleTuple<_U1, _U2>(),
-	bool>::type = true>
-        constexpr tuple(const pair<_U1, _U2>& __in)
-	: _Inherited(__in.first, __in.second) { }
-
-      template<typename _U1, typename _U2, typename
-        enable_if<_TMC::template
-                    _ConstructibleTuple<_U1, _U2>()
-                  && !_TMC::template
-                    _ImplicitlyConvertibleTuple<_U1, _U2>(),
-	bool>::type = false>
-        explicit constexpr tuple(const pair<_U1, _U2>& __in)
-	: _Inherited(__in.first, __in.second) { }
-
-      template<typename _U1, typename _U2, typename
-        enable_if<_TMC::template
-                    _MoveConstructibleTuple<_U1, _U2>()
-                  && _TMC::template
-                    _ImplicitlyMoveConvertibleTuple<_U1, _U2>(),
-	bool>::type = true>
-        constexpr tuple(pair<_U1, _U2>&& __in)
-	: _Inherited(std::forward<_U1>(__in.first),
-		     std::forward<_U2>(__in.second)) { }
-
-      template<typename _U1, typename _U2, typename
-        enable_if<_TMC::template
-                    _MoveConstructibleTuple<_U1, _U2>()
-                  && !_TMC::template
-                    _ImplicitlyMoveConvertibleTuple<_U1, _U2>(),
-	bool>::type = false>
-        explicit constexpr tuple(pair<_U1, _U2>&& __in)
-	: _Inherited(std::forward<_U1>(__in.first),
-		     std::forward<_U2>(__in.second)) { }
-
-      // Allocator-extended constructors.
-
-      template<typename _Alloc>
-	tuple(allocator_arg_t __tag, const _Alloc& __a)
-	: _Inherited(__tag, __a) { }
-
-      template<typename _Alloc, typename _Dummy = void,
-               typename enable_if<
-                 _TCC<_Dummy>::template
-                   _ConstructibleTuple<_T1, _T2>()
-                 && _TCC<_Dummy>::template
-                   _ImplicitlyConvertibleTuple<_T1, _T2>(),
-               bool>::type=true>
-
-	tuple(allocator_arg_t __tag, const _Alloc& __a,
-	      const _T1& __a1, const _T2& __a2)
-	: _Inherited(__tag, __a, __a1, __a2) { }
-
-      template<typename _Alloc, typename _Dummy = void,
-               typename enable_if<
-                 _TCC<_Dummy>::template
-                   _ConstructibleTuple<_T1, _T2>()
-                 && !_TCC<_Dummy>::template
-                   _ImplicitlyConvertibleTuple<_T1, _T2>(),
-               bool>::type=false>
-
-	explicit tuple(allocator_arg_t __tag, const _Alloc& __a,
-	      const _T1& __a1, const _T2& __a2)
-	: _Inherited(__tag, __a, __a1, __a2) { }
-
-      template<typename _Alloc, typename _U1, typename _U2, typename
-        enable_if<_TMC::template
-                    _MoveConstructibleTuple<_U1, _U2>()
-                  && _TMC::template
-                    _ImplicitlyMoveConvertibleTuple<_U1, _U2>(),
-	bool>::type = true>
-	tuple(allocator_arg_t __tag, const _Alloc& __a, _U1&& __a1, _U2&& __a2)
-	: _Inherited(__tag, __a, std::forward<_U1>(__a1),
-	             std::forward<_U2>(__a2)) { }
-
-      template<typename _Alloc, typename _U1, typename _U2, typename
-        enable_if<_TMC::template
-                    _MoveConstructibleTuple<_U1, _U2>()
-                  && !_TMC::template
-                    _ImplicitlyMoveConvertibleTuple<_U1, _U2>(),
-	bool>::type = false>
-	explicit tuple(allocator_arg_t __tag, const _Alloc& __a,
-                       _U1&& __a1, _U2&& __a2)
-	: _Inherited(__tag, __a, std::forward<_U1>(__a1),
-	             std::forward<_U2>(__a2)) { }
-
-      template<typename _Alloc>
-	tuple(allocator_arg_t __tag, const _Alloc& __a, const tuple& __in)
-	: _Inherited(__tag, __a, static_cast<const _Inherited&>(__in)) { }
-
-      template<typename _Alloc>
-	tuple(allocator_arg_t __tag, const _Alloc& __a, tuple&& __in)
-	: _Inherited(__tag, __a, static_cast<_Inherited&&>(__in)) { }
-
-      template<typename _Alloc, typename _U1, typename _U2, typename
-        enable_if<_TMC::template
-                    _ConstructibleTuple<_U1, _U2>()
-                  && _TMC::template
-                    _ImplicitlyConvertibleTuple<_U1, _U2>(),
-	bool>::type = true>
-	tuple(allocator_arg_t __tag, const _Alloc& __a,
-	      const tuple<_U1, _U2>& __in)
-	: _Inherited(__tag, __a,
-	             static_cast<const _Tuple_impl<0, _U1, _U2>&>(__in))
-	{ }
-
-      template<typename _Alloc, typename _U1, typename _U2, typename
-        enable_if<_TMC::template
-                    _ConstructibleTuple<_U1, _U2>()
-                  && !_TMC::template
-                    _ImplicitlyConvertibleTuple<_U1, _U2>(),
-	bool>::type = false>
-	explicit tuple(allocator_arg_t __tag, const _Alloc& __a,
-	      const tuple<_U1, _U2>& __in)
-	: _Inherited(__tag, __a,
-	             static_cast<const _Tuple_impl<0, _U1, _U2>&>(__in))
-	{ }
-
-      template<typename _Alloc, typename _U1, typename _U2, typename
-        enable_if<_TMC::template
-                    _MoveConstructibleTuple<_U1, _U2>()
-                  && _TMC::template
-                    _ImplicitlyMoveConvertibleTuple<_U1, _U2>(),
-	bool>::type = true>
-	tuple(allocator_arg_t __tag, const _Alloc& __a, tuple<_U1, _U2>&& __in)
-	: _Inherited(__tag, __a, static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in))
-	{ }
-
-      template<typename _Alloc, typename _U1, typename _U2, typename
-        enable_if<_TMC::template
-                    _MoveConstructibleTuple<_U1, _U2>()
-                  && !_TMC::template
-                    _ImplicitlyMoveConvertibleTuple<_U1, _U2>(),
-	bool>::type = false>
-	explicit tuple(allocator_arg_t __tag, const _Alloc& __a,
-                       tuple<_U1, _U2>&& __in)
-	: _Inherited(__tag, __a, static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in))
-	{ }
-
-      template<typename _Alloc, typename _U1, typename _U2, typename
-        enable_if<_TMC::template
-                    _ConstructibleTuple<_U1, _U2>()
-                  && _TMC::template
-                    _ImplicitlyConvertibleTuple<_U1, _U2>(),
-	bool>::type = true>
-        tuple(allocator_arg_t __tag, const _Alloc& __a,
-	      const pair<_U1, _U2>& __in)
-	: _Inherited(__tag, __a, __in.first, __in.second) { }
-
-      template<typename _Alloc, typename _U1, typename _U2, typename
-        enable_if<_TMC::template
-                    _ConstructibleTuple<_U1, _U2>()
-                  && !_TMC::template
-                    _ImplicitlyConvertibleTuple<_U1, _U2>(),
-	bool>::type = false>
-        explicit tuple(allocator_arg_t __tag, const _Alloc& __a,
-	      const pair<_U1, _U2>& __in)
-	: _Inherited(__tag, __a, __in.first, __in.second) { }
-
-      template<typename _Alloc, typename _U1, typename _U2, typename
-        enable_if<_TMC::template
-                    _MoveConstructibleTuple<_U1, _U2>()
-                  && _TMC::template
-                    _ImplicitlyMoveConvertibleTuple<_U1, _U2>(),
-	bool>::type = true>
-        tuple(allocator_arg_t __tag, const _Alloc& __a, pair<_U1, _U2>&& __in)
-	: _Inherited(__tag, __a, std::forward<_U1>(__in.first),
-		     std::forward<_U2>(__in.second)) { }
-
-      template<typename _Alloc, typename _U1, typename _U2, typename
-        enable_if<_TMC::template
-                    _MoveConstructibleTuple<_U1, _U2>()
-                  && !_TMC::template
-                    _ImplicitlyMoveConvertibleTuple<_U1, _U2>(),
-	bool>::type = false>
-        explicit tuple(allocator_arg_t __tag, const _Alloc& __a,
-                       pair<_U1, _U2>&& __in)
-	: _Inherited(__tag, __a, std::forward<_U1>(__in.first),
-		     std::forward<_U2>(__in.second)) { }
-
-      tuple&
-      operator=(typename conditional<__assignable<const _T1&, const _T2&>(),
-				     const tuple&,
-				     const __nonesuch_no_braces&>::type __in)
-      noexcept(__nothrow_assignable<const _T1&, const _T2&>())
-      {
-	static_cast<_Inherited&>(*this) = __in;
-	return *this;
-      }
-
-      tuple&
-      operator=(typename conditional<__assignable<_T1, _T2>(),
-				     tuple&&,
-				     __nonesuch_no_braces&&>::type __in)
-      noexcept(__nothrow_assignable<_T1, _T2>())
-      {
-	static_cast<_Inherited&>(*this) = std::move(__in);
-	return *this;
-      }
-
-      template<typename _U1, typename _U2>
-	__enable_if_t<__assignable<const _U1&, const _U2&>(), tuple&>
-	operator=(const tuple<_U1, _U2>& __in)
-	noexcept(__nothrow_assignable<const _U1&, const _U2&>())
-	{
-	  static_cast<_Inherited&>(*this) = __in;
-	  return *this;
-	}
-
-      template<typename _U1, typename _U2>
-	__enable_if_t<__assignable<_U1, _U2>(), tuple&>
-	operator=(tuple<_U1, _U2>&& __in)
-	noexcept(__nothrow_assignable<_U1, _U2>())
-	{
-	  static_cast<_Inherited&>(*this) = std::move(__in);
-	  return *this;
-	}
-
-      template<typename _U1, typename _U2>
-	__enable_if_t<__assignable<const _U1&, const _U2&>(), tuple&>
-	operator=(const pair<_U1, _U2>& __in)
-	noexcept(__nothrow_assignable<const _U1&, const _U2&>())
-	{
-	  this->_M_head(*this) = __in.first;
-	  this->_M_tail(*this)._M_head(*this) = __in.second;
-	  return *this;
-	}
-
-      template<typename _U1, typename _U2>
-	__enable_if_t<__assignable<_U1, _U2>(), tuple&>
-	operator=(pair<_U1, _U2>&& __in)
-	noexcept(__nothrow_assignable<_U1, _U2>())
-	{
-	  this->_M_head(*this) = std::forward<_U1>(__in.first);
-	  this->_M_tail(*this)._M_head(*this) = std::forward<_U2>(__in.second);
-	  return *this;
-	}
-
-      void
-      swap(tuple& __in)
-      noexcept(noexcept(__in._M_swap(__in)))
-      { _Inherited::_M_swap(__in); }
-    };
-
-
   /// class tuple_size
   template<typename... _Elements>
     struct tuple_size<tuple<_Elements...>>