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.
commit 6fea64cd9f546e587c7212c7b95fde0b3005f936 Author: Jonathan Wakely <jwak...@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...>>