From patchwork Fri Aug 17 19:29:13 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 959088 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-483886-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="MR/sjnPb"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 41sYDd3g83z9s5c for ; Sat, 18 Aug 2018 05:29:37 +1000 (AEST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:mime-version:content-type; q=dns; s=default; b=kekXCfONGXitAWJ1/V3FBgDIa4v7e2m+4H3/LX9E1eDwWeAd5a ZiyLYiqoH/su/RD2Y0u3rkipKceIECEMYMI0WLvrU+sg2hfQC8Rl399jHsZusXDd f8cr4br4O96V/HXghz4B+JlgJoqLlJkK+EQLjnbyQN6NEmK1CektTzq1c= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:mime-version:content-type; s= default; bh=LJoEe4L4XTuj3kco/WJ0Ph7wuhQ=; b=MR/sjnPbFrQ6b5nZCPU3 ESxFTeAvazK0p8AgbdkYiU83RNuHzq6Q7DsHtpjmxIPDX3E4+6xOr8gpYpu5v6Ct diImCxETtCfrvsQZX5Ut+TsGeQmIhTaVz4vEniIq4o1JXsA/gIMrn2z4Anu3U9V9 ImFi0UAXNgDTc3u/ba0Cfcs= Received: (qmail 85225 invoked by alias); 17 Aug 2018 19:29:20 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 85195 invoked by uid 89); 17 Aug 2018 19:29:19 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-26.0 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=7648 X-HELO: mx1.redhat.com Received: from mx3-rdu2.redhat.com (HELO mx1.redhat.com) (66.187.233.73) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 17 Aug 2018 19:29:16 +0000 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 5D8994076076; Fri, 17 Aug 2018 19:29:14 +0000 (UTC) Received: from localhost (unknown [10.33.36.38]) by smtp.corp.redhat.com (Postfix) with ESMTP id F38A310193; Fri, 17 Aug 2018 19:29:13 +0000 (UTC) Date: Fri, 17 Aug 2018 20:29:13 +0100 From: Jonathan Wakely To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Cc: Ville Voutilainen Subject: [PATCH] RFC: remove std::tuple partial specialization Message-ID: <20180817192913.GQ13845@redhat.com> MIME-Version: 1.0 Content-Disposition: inline X-Clacks-Overhead: GNU Terry Pratchett User-Agent: Mutt/1.9.2 (2017-12-15) 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 t(std::allocator_arg, std::allocator{}); 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() - && _TMC::template - _ImplicitlyMoveConvertibleTuple<_U1, _U2>() - && !is_same::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 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 Date: Fri Aug 17 19:07:36 2018 +0100 Remove std::tuple 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<_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_> >::value; } + template static constexpr bool _NotSameTuple() { @@ -544,6 +539,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return true; } + template 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; + + template() + && _TP::template _ImplicitlyConvertibleTuple<_U1, _U2>(), + bool> = true> + constexpr tuple(const pair<_U1, _U2>& __in) + : _Inherited(__in.first, __in.second) { } + + template() + && ! _TP::template _ImplicitlyConvertibleTuple<_U1, _U2>(), + bool> = false> + explicit constexpr tuple(const pair<_U1, _U2>& __in) + : _Inherited(__in.first, __in.second) { } + + template() + && _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() + && ! _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 @@ -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::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&&>(), 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&>(__in)) @@ -852,12 +884,53 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION && _TNTC<_Dummy>::template _NonNestedTuple&&>(), 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() + && _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() + && ! _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() + && _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() + && ! _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 tuple&, @@ -896,9 +969,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return *this; } + template + __enable_if_t<__assignable(), tuple&> + operator=(const pair<_U1, _U2>& __in) + noexcept(__nothrow_assignable()) + { + this->_M_head(*this) = __in.first; + this->_M_tail(*this)._M_head(*this) = __in.second; + return *this; + } + + template + __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 - class tuple<_T1, _T2> - : public _Tuple_impl<0, _T1, _T2>, - private __tuple_base> - { - typedef _Tuple_impl<0, _T1, _T2> _Inherited; - - template - static constexpr bool __assignable() - { - return __and_, - is_assignable<_T2&, _U2>>::value; - } - - template - static constexpr bool __nothrow_assignable() - { - return __and_, - is_nothrow_assignable<_T2&, _U2>>::value; - } - - public: - template , - __is_implicitly_default_constructible<_U2>> - ::value, bool>::type = true> - constexpr tuple() - : _Inherited() { } - - template , - 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 using _TCC = - _TC::value, _T1, _T2>; - - template::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::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; - - template() - && _TMC::template - _ImplicitlyMoveConvertibleTuple<_U1, _U2>() - && !is_same::type, - allocator_arg_t>::value, - bool>::type = true> - constexpr tuple(_U1&& __a1, _U2&& __a2) - : _Inherited(std::forward<_U1>(__a1), std::forward<_U2>(__a2)) { } - - template() - && !_TMC::template - _ImplicitlyMoveConvertibleTuple<_U1, _U2>() - && !is_same::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() - && _TMC::template - _ImplicitlyConvertibleTuple<_U1, _U2>(), - bool>::type = true> - constexpr tuple(const tuple<_U1, _U2>& __in) - : _Inherited(static_cast&>(__in)) { } - - template() - && !_TMC::template - _ImplicitlyConvertibleTuple<_U1, _U2>(), - bool>::type = false> - explicit constexpr tuple(const tuple<_U1, _U2>& __in) - : _Inherited(static_cast&>(__in)) { } - - template() - && _TMC::template - _ImplicitlyMoveConvertibleTuple<_U1, _U2>(), - bool>::type = true> - constexpr tuple(tuple<_U1, _U2>&& __in) - : _Inherited(static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in)) { } - - template() - && !_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() - && _TMC::template - _ImplicitlyConvertibleTuple<_U1, _U2>(), - bool>::type = true> - constexpr tuple(const pair<_U1, _U2>& __in) - : _Inherited(__in.first, __in.second) { } - - template() - && !_TMC::template - _ImplicitlyConvertibleTuple<_U1, _U2>(), - bool>::type = false> - explicit constexpr tuple(const pair<_U1, _U2>& __in) - : _Inherited(__in.first, __in.second) { } - - template() - && _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() - && !_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 - tuple(allocator_arg_t __tag, const _Alloc& __a) - : _Inherited(__tag, __a) { } - - template::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::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() - && _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() - && !_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 - tuple(allocator_arg_t __tag, const _Alloc& __a, const tuple& __in) - : _Inherited(__tag, __a, static_cast(__in)) { } - - template - tuple(allocator_arg_t __tag, const _Alloc& __a, tuple&& __in) - : _Inherited(__tag, __a, static_cast<_Inherited&&>(__in)) { } - - template() - && _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&>(__in)) - { } - - template() - && !_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&>(__in)) - { } - - template() - && _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() - && !_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() - && _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() - && !_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() - && _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() - && !_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 tuple&, - const __nonesuch_no_braces&>::type __in) - noexcept(__nothrow_assignable()) - { - 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 - __enable_if_t<__assignable(), tuple&> - operator=(const tuple<_U1, _U2>& __in) - noexcept(__nothrow_assignable()) - { - static_cast<_Inherited&>(*this) = __in; - return *this; - } - - template - __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 - __enable_if_t<__assignable(), tuple&> - operator=(const pair<_U1, _U2>& __in) - noexcept(__nothrow_assignable()) - { - this->_M_head(*this) = __in.first; - this->_M_tail(*this)._M_head(*this) = __in.second; - return *this; - } - - template - __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 struct tuple_size>