This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] RFC: remove std::tuple<T, U> partial specialization
- From: Jonathan Wakely <jwakely at redhat dot com>
- To: libstdc++ at gcc dot gnu dot org, gcc-patches at gcc dot gnu dot org
- Cc: Ville Voutilainen <ville dot voutilainen at gmail dot com>
- Date: Fri, 17 Aug 2018 20:29:13 +0100
- Subject: [PATCH] RFC: remove std::tuple<T, U> partial specialization
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 <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...>>