Re: [PATCH] PR libstdc++/86963 Implement LWG 2729 constraints on tuple assignment
On 17/08/18 19:54 +0100, Jonathan Wakely wrote: On 17/08/18 19:01 +0100, Jonathan Wakely wrote: On 17/08/18 18:52 +0100, Jonathan Wakely wrote: + // The tag parameter ensures that in nested tuples each __tuple_base + // is a different type and can use the empty base-class optimisation. + template +class __tuple_base Specifically, this would fail if __tuple_base was not a class template: static_assert(sizeof(tuple>) == sizeof(int), ""); And also: struct empty {}; static_assert(sizeof(tuple, tuple>) == 2, ""); In fact, it's just occurred to me that we don't really need the __tuple_base class template at all. We can make _Tuple_impl non-assignable (adding _M_assign members for the required functionality). Then we don't need an extra base class. Which is what this patch does. I think it's cleaner than needing the __tuple_base base class. Tested x86_64-linux and committed to trunk. commit 1af2f8e775e0f742b530912a3e988316f2c74375 Author: Jonathan Wakely Date: Fri Aug 17 20:37:29 2018 +0100 PR libstdc++/86963 Remove use of __tuple_base in std::tuple The _Tuple_impl base class can be used to disable copy/move assignment, without requiring an extra base class. Exception specifications on std::tuple assignment and swap functions can be defined directly using is_nothrow_swappable, instead of querying the base classes. PR libstdc++/86963 * include/std/tuple (_Tuple_impl::operator=): Define as deleted. (_Tuple_impl::_M_assign): New functions to perform assignment instead of assignment operators. (_Tuple_impl::_M_swap): Remove exception specification. (_Tuple_impl<_Idx, _Head>): Likewise. (_TC::_NonNestedTuple, _TC::_NotSameTuple): Use __remove_cvref_t. (__tuple_base): Remove. (tuple, tuple<_T1, _T2>): Remove inheritance from __tuple_base. (tuple::operator=, tuple<_T1, _T2>::operator=): Call _M_assign. (tuple::swap, tuple<_T1, _T2>::swap): Define exception specification using __is_nothrow_swappable. (tuple<_T1, _T2>::tuple(_U1&&, _U2&&)): Use __remove_cvref_t. diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple index 955b853066f..56b97c25eed 100644 --- a/libstdc++-v3/include/std/tuple +++ b/libstdc++-v3/include/std/tuple @@ -219,6 +219,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION constexpr _Tuple_impl(const _Tuple_impl&) = default; + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 2729. Missing SFINAE on std::pair::operator= + _Tuple_impl& operator=(const _Tuple_impl&) = delete; + constexpr _Tuple_impl(_Tuple_impl&& __in) noexcept(__and_, @@ -288,49 +292,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION std::forward<_UHead> (_Tuple_impl<_Idx, _UHead, _UTails...>::_M_head(__in))) { } - _Tuple_impl& - operator=(const _Tuple_impl& __in) - { - _M_head(*this) = _M_head(__in); - _M_tail(*this) = _M_tail(__in); - return *this; - } - - _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)); - return *this; - } - template -_Tuple_impl& -operator=(const _Tuple_impl<_Idx, _UElements...>& __in) +void +_M_assign(const _Tuple_impl<_Idx, _UElements...>& __in) { _M_head(*this) = _Tuple_impl<_Idx, _UElements...>::_M_head(__in); - _M_tail(*this) = _Tuple_impl<_Idx, _UElements...>::_M_tail(__in); - return *this; + _M_tail(*this)._M_assign( + _Tuple_impl<_Idx, _UElements...>::_M_tail(__in)); } template -_Tuple_impl& -operator=(_Tuple_impl<_Idx, _UHead, _UTails...>&& __in) +void +_M_assign(_Tuple_impl<_Idx, _UHead, _UTails...>&& __in) { _M_head(*this) = std::forward<_UHead> (_Tuple_impl<_Idx, _UHead, _UTails...>::_M_head(__in)); - _M_tail(*this) = std::move - (_Tuple_impl<_Idx, _UHead, _UTails...>::_M_tail(__in)); - return *this; + _M_tail(*this)._M_assign( + std::move(_Tuple_impl<_Idx, _UHead, _UTails...>::_M_tail(__in))); } 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)); @@ -367,6 +350,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION constexpr _Tuple_impl(const _Tuple_impl&) = default; + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 2729. Missing SFINAE on std::pair::operator= + _Tuple_impl& operator=(const _Tuple_impl&) = delete; + constexpr _Tuple_impl(_Tuple_impl&& __in) noexcept(is_nothrow_move_constructible<_Head>::value) @@ -420,42 +407,24 @@
Re: [PATCH] PR libstdc++/86963 Implement LWG 2729 constraints on tuple assignment
On 17/08/18 19:01 +0100, Jonathan Wakely wrote: On 17/08/18 18:52 +0100, Jonathan Wakely wrote: + // The tag parameter ensures that in nested tuples each __tuple_base + // is a different type and can use the empty base-class optimisation. + template +class __tuple_base Specifically, this would fail if __tuple_base was not a class template: static_assert(sizeof(tuple>) == sizeof(int), ""); And also: struct empty {}; static_assert(sizeof(tuple, tuple>) == 2, ""); In fact, it's just occurred to me that we don't really need the __tuple_base class template at all. We can make _Tuple_impl non-assignable (adding _M_assign members for the required functionality). Then we don't need an extra base class.
Re: [PATCH] PR libstdc++/86963 Implement LWG 2729 constraints on tuple assignment
On 17/08/18 18:52 +0100, Jonathan Wakely wrote: + // The tag parameter ensures that in nested tuples each __tuple_base + // is a different type and can use the empty base-class optimisation. + template +class __tuple_base Specifically, this would fail if __tuple_base was not a class template: static_assert(sizeof(tuple>) == sizeof(int), ""); And also: struct empty {}; static_assert(sizeof(tuple, tuple>) == 2, "");
[PATCH] PR libstdc++/86963 Implement LWG 2729 constraints on tuple assignment
PR libstdc++/86963 * include/std/tuple (__tuple_base): New class template with deleted copy assignment operator. (tuple, tuple<_T1, _T2>): Derive from __tuple_base so that implicit copy/move assignment operator will be deleted/suppressed. (tuple::__assignable, tuple<_T1, _T2>::__assignable): New helper functions for SFINAE constraints on assignment operators. (tuple::__nothrow_assignable, tuple<_T1, _T2>::__nothrow_assignable): New helper functions for exception specifications. (tuple::operator=(const tuple&), tuple::operator=(tuple&&)) (tuple<_T1, _T2>::operator=(const tuple&)) (tuple<_T1, _T2>::operator=(tuple&&)): Change parameter types to __nonesuch_no_braces when the operator should be defined implicitly. Use __nothrow_assignable for exception specifications. (tuple::operator=(const tuple<_UElements...>&)) (tuple::operator=(tuple<_UElements...>&&)) (tuple<_T1, _T2>::operator=(const tuple<_U1, _U2>&)) (tuple<_T1, _T2>::operator=(tuple<_U1, _U2>&&)) (tuple<_T1, _T2>::operator=(const pair<_U1, _U2>&)) (tuple<_T1, _T2>::operator=(pair<_U1, _U2>&&)): Constrain using __assignable and use __nothrow_assignable for exception specifications. * python/libstdcxx/v6/printers.py (is_specialization_of): Accept gdb.Type as first argument, instead of a string. (StdTuplePrinter._iterator._is_nonempty_tuple): New method to check tuple for expected structure. (StdTuplePrinter._iterator.__init__): Use _is_nonempty_tuple. * testsuite/20_util/tuple/dr2729.cc: New test. * testsuite/20_util/tuple/element_access/get_neg.cc: Change dg-error to dg-prune-output. Tested x86_64-linux, committed to trunk. commit 6f8a632c16f417ac0e0db1b2a02a27708702279d Author: Jonathan Wakely Date: Fri Aug 17 16:12:21 2018 +0100 PR libstdc++/86963 Implement LWG 2729 constraints on tuple assignment PR libstdc++/86963 * include/std/tuple (__tuple_base): New class template with deleted copy assignment operator. (tuple, tuple<_T1, _T2>): Derive from __tuple_base so that implicit copy/move assignment operator will be deleted/suppressed. (tuple::__assignable, tuple<_T1, _T2>::__assignable): New helper functions for SFINAE constraints on assignment operators. (tuple::__nothrow_assignable, tuple<_T1, _T2>::__nothrow_assignable): New helper functions for exception specifications. (tuple::operator=(const tuple&), tuple::operator=(tuple&&)) (tuple<_T1, _T2>::operator=(const tuple&)) (tuple<_T1, _T2>::operator=(tuple&&)): Change parameter types to __nonesuch_no_braces when the operator should be defined implicitly. Use __nothrow_assignable for exception specifications. (tuple::operator=(const tuple<_UElements...>&)) (tuple::operator=(tuple<_UElements...>&&)) (tuple<_T1, _T2>::operator=(const tuple<_U1, _U2>&)) (tuple<_T1, _T2>::operator=(tuple<_U1, _U2>&&)) (tuple<_T1, _T2>::operator=(const pair<_U1, _U2>&)) (tuple<_T1, _T2>::operator=(pair<_U1, _U2>&&)): Constrain using __assignable and use __nothrow_assignable for exception specifications. * python/libstdcxx/v6/printers.py (is_specialization_of): Accept gdb.Type as first argument, instead of a string. (StdTuplePrinter._iterator._is_nonempty_tuple): New method to check tuple for expected structure. (StdTuplePrinter._iterator.__init__): Use _is_nonempty_tuple. * testsuite/20_util/tuple/dr2729.cc: New test. * testsuite/20_util/tuple/element_access/get_neg.cc: Change dg-error to dg-prune-output. diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple index dd7daf7f1cf..955b853066f 100644 --- a/libstdc++-v3/include/std/tuple +++ b/libstdc++-v3/include/std/tuple @@ -551,9 +551,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } }; + // The tag parameter ensures that in nested tuples each __tuple_base + // is a different type and can use the empty base-class optimisation. + template +class __tuple_base +{ + template friend struct tuple; + __tuple_base() = default; + ~__tuple_base() = default; + __tuple_base(const __tuple_base&) = default; + __tuple_base& operator=(const __tuple_base&) = delete; +}; + /// Primary class template, tuple template -class tuple : public _Tuple_impl<0, _Elements...> +class tuple +: public _Tuple_impl<0, _Elements...>, + private __tuple_base> { typedef _Tuple_impl<0, _Elements...> _Inherited; @@ -573,6 +587,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } }; + template +