First, pardons all around if I'm completely repeating what Marc already tried to do. I think I'm taking a different approach.
I'm not adding any defaulted or deleted functions, so I don't think I'm changing triviality. Chances are I need to actually delete the copy operations I want to get rid of, though; currently they are defined with a conditional parameter type. That leaves open the possibility of a move operation being implicitly defaulted rather than suppressed, which can make them trivial where they weren't before. I'm not sure whether this has abi impact. In the sense that this is making previously-defined move operations suppressed, maybe. I guess I could alternatively try making them conditional, and making the inverse of the conditional private; that should keep the traits working and retain abi compatibility, because I don't think our toolchains at least on non-Windows platforms include access levels in mangling. Tested for tuple on Linux-x64. Thoughts? 2016-08-30 Ville Voutilainen <ville.voutilai...@gmail.com> Implement LWG 2729 for tuple. * include/std/tuple (_Tuple_impl(_Tuple_impl&&)): Suppress conditionally. (_Tuple_impl(_Tuple_impl<_Idx, _UHead, _UTails...>&&)): Likewise. (__is_tuple_impl_trait_impl, __is_tuple_impl_trait): New. (_Tuple_impl(const _Head&)): Constrain. (_Tuple_impl(_UHead&&)): Likewise. (_Tuple_impl(_Tuple_impl&&)): Suppress conditionally. (_Tuple_impl(const _Tuple_impl<_Idx, _UHead>&)): Constrain. (_Tuple_impl(_Tuple_impl<_Idx, _UHead>&&)): Likewise. (operator=(const tuple&)): Enable conditionally. (operator=(tuple&&)): Suppress conditionally. (operator=(const tuple<_UElements...>&)): Constrain. (operator=(tuple<_UElements...>&&)): Likewise. (operator=(const tuple&)): Enable conditionally (2-param tuple). (operator=(tuple&&)): Suppress conditionally (2-param tuple). (operator=(const tuple<_U1, _U2>&)): Constrain. (operator=(tuple<_U1, _U2>&&)): Likewise. (operator=(const pair<_U1, _U2>&)): Likewise. (operator=(pair<_U1, _U2>&&)): Likewise. * testsuite/20_util/tuple/element_access/get_neg.cc: Adjust. * testsuite/20_util/tuple/tuple_traits.cc: New.
diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple index c06a040..9f43732 100644 --- a/libstdc++-v3/include/std/tuple +++ b/libstdc++-v3/include/std/tuple @@ -220,8 +220,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION constexpr _Tuple_impl(const _Tuple_impl&) = default; constexpr - _Tuple_impl(_Tuple_impl&& __in) - noexcept(__and_<is_nothrow_move_constructible<_Head>, + _Tuple_impl(typename conditional< + __and_<is_move_constructible<_Head>, + is_move_constructible<_Inherited>>::value, + _Tuple_impl&&, __nonesuch&&>::type __in) + noexcept(__and_<is_nothrow_move_constructible<_Head>, is_nothrow_move_constructible<_Inherited>>::value) : _Inherited(std::move(_M_tail(__in))), _Base(std::forward<_Head>(_M_head(__in))) { } @@ -232,7 +235,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Base(_Tuple_impl<_Idx, _UElements...>::_M_head(__in)) { } template<typename _UHead, typename... _UTails> - constexpr _Tuple_impl(_Tuple_impl<_Idx, _UHead, _UTails...>&& __in) + constexpr _Tuple_impl(typename conditional< + __and_<is_move_constructible<_Head>, + is_move_constructible<_Inherited>>::value, + _Tuple_impl<_Idx, _UHead, _UTails...>&&, + __nonesuch&&>::type __in) : _Inherited(std::move (_Tuple_impl<_Idx, _UHead, _UTails...>::_M_tail(__in))), _Base(std::forward<_UHead> @@ -338,6 +345,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } }; + template<typename...> + struct __is_tuple_impl_trait_impl : false_type + { }; + + template<std::size_t _Idx, typename... _Tp> + struct __is_tuple_impl_trait_impl<_Tuple_impl<_Idx, _Tp...>> : true_type + { }; + + template<typename _Tp> + struct __is_tuple_impl_trait : public __is_tuple_impl_trait_impl<_Tp> + { }; + // Basis case of inheritance recursion. template<std::size_t _Idx, typename _Head> struct _Tuple_impl<_Idx, _Head> @@ -356,11 +375,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION constexpr _Tuple_impl() : _Base() { } + template<typename _Dummy=void, + typename enable_if<is_constructible<_Base, const _Head&>::value, + bool>::type=true> explicit constexpr _Tuple_impl(const _Head& __head) : _Base(__head) { } - template<typename _UHead> + template<typename _UHead, + typename enable_if<__and_<is_constructible<_Base, _UHead&&>, + __not_<__is_tuple_impl_trait< + typename + remove_reference<_UHead>::type>> + >::value, + bool>::type = true> explicit constexpr _Tuple_impl(_UHead&& __head) : _Base(std::forward<_UHead>(__head)) { } @@ -368,15 +396,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION constexpr _Tuple_impl(const _Tuple_impl&) = default; constexpr - _Tuple_impl(_Tuple_impl&& __in) + _Tuple_impl(typename conditional< + is_move_constructible<_Head>::value, + _Tuple_impl&&, __nonesuch&&>::type __in) noexcept(is_nothrow_move_constructible<_Head>::value) : _Base(std::forward<_Head>(_M_head(__in))) { } - template<typename _UHead> + template<typename _UHead, + typename enable_if<!is_same<_Head, _UHead>::value, + bool>::type = true> constexpr _Tuple_impl(const _Tuple_impl<_Idx, _UHead>& __in) : _Base(_Tuple_impl<_Idx, _UHead>::_M_head(__in)) { } - template<typename _UHead> + template<typename _UHead, + typename enable_if<!is_same<_Head, _UHead>::value, + bool>::type = true> constexpr _Tuple_impl(_Tuple_impl<_Idx, _UHead>&& __in) : _Base(std::forward<_UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in))) { } @@ -832,14 +866,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { } tuple& - operator=(const tuple& __in) + operator=(typename + conditional<__and_<is_copy_assignable<_Elements>...>::value, + const tuple&, const __nonesuch&>::type __in) { static_cast<_Inherited&>(*this) = __in; return *this; } tuple& - operator=(tuple&& __in) + operator=(typename + conditional<__and_<is_move_assignable<_Elements>...>::value, + tuple&&, __nonesuch&&>::type __in) noexcept(is_nothrow_move_assignable<_Inherited>::value) { static_cast<_Inherited&>(*this) = std::move(__in); @@ -848,7 +886,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename... _UElements, typename = typename enable_if<sizeof...(_UElements) - == sizeof...(_Elements)>::type> + == sizeof...(_Elements) + && + __and_<is_assignable<_Elements&, + const _UElements&>...>::value>::type> tuple& operator=(const tuple<_UElements...>& __in) { @@ -858,7 +899,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename... _UElements, typename = typename enable_if<sizeof...(_UElements) - == sizeof...(_Elements)>::type> + == sizeof...(_Elements) + && + __and_<is_assignable<_Elements&, + _UElements&&>...>::value>::type> tuple& operator=(tuple<_UElements...>&& __in) { @@ -1189,14 +1233,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION std::forward<_U2>(__in.second)) { } tuple& - operator=(const tuple& __in) + operator=(typename + conditional<__and_<is_copy_assignable<_T1>, + is_copy_assignable<_T2>>::value, + const tuple&, const __nonesuch&>::type __in) { static_cast<_Inherited&>(*this) = __in; return *this; } tuple& - operator=(tuple&& __in) + operator=(typename + conditional<__and_<is_move_assignable<_T1>, + is_move_assignable<_T2>>::value, + tuple&&, __nonesuch&&>::type __in) noexcept(is_nothrow_move_assignable<_Inherited>::value) { static_cast<_Inherited&>(*this) = std::move(__in); @@ -1204,7 +1254,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename _U1, typename _U2> - tuple& + typename + enable_if<__and_<is_assignable<_T1&, const _U1&>, + is_assignable<_T2&, const _U2&>>::value, + tuple&>::type operator=(const tuple<_U1, _U2>& __in) { static_cast<_Inherited&>(*this) = __in; @@ -1212,7 +1265,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename _U1, typename _U2> - tuple& + typename + enable_if<__and_<is_assignable<_T1&, _U1&&>, + is_assignable<_T2&, _U2&&>>::value, + tuple&>::type operator=(tuple<_U1, _U2>&& __in) { static_cast<_Inherited&>(*this) = std::move(__in); @@ -1220,7 +1276,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename _U1, typename _U2> - tuple& + typename + enable_if<__and_<is_assignable<_T1&, const _U1&>, + is_assignable<_T2&, const _U2&>>::value, + tuple&>::type operator=(const pair<_U1, _U2>& __in) { this->_M_head(*this) = __in.first; @@ -1229,7 +1288,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename _U1, typename _U2> - tuple& + typename + enable_if<__and_<is_assignable<_T1&, _U1&&>, + is_assignable<_T2&, _U2&&>>::value, + tuple&>::type operator=(pair<_U1, _U2>&& __in) { this->_M_head(*this) = std::forward<_U1>(__in.first); diff --git a/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc b/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc index 5bcf576..1c08d45 100644 --- a/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc +++ b/libstdc++-v3/testsuite/20_util/tuple/element_access/get_neg.cc @@ -17,7 +17,7 @@ // { dg-options "-fno-show-column" } // { dg-do compile { target c++14 } } -// { dg-error "in range" "" { target *-*-* } 1280 } +// { dg-error "in range" "" { target *-*-* } 1342 } #include <tuple> diff --git a/libstdc++-v3/testsuite/20_util/tuple/tuple_traits.cc b/libstdc++-v3/testsuite/20_util/tuple/tuple_traits.cc new file mode 100644 index 0000000..b4dde99 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/tuple/tuple_traits.cc @@ -0,0 +1,50 @@ +// { dg-do compile { target c++11 } } + +// Copyright (C) 2016 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +#include <tuple> +#include <type_traits> +#include <utility> + +using namespace std; + +struct Poison +{ + Poison(Poison&&) = delete; +}; + + +int main() +{ + static_assert(!is_copy_constructible<Poison>::value, ""); + static_assert(!is_move_constructible<Poison>::value, ""); + static_assert(!is_copy_assignable<Poison>::value, ""); + static_assert(!is_move_assignable<Poison>::value, ""); + static_assert(!is_copy_constructible<std::tuple<int, Poison>>::value, ""); + static_assert(!is_move_constructible<std::tuple<int, Poison>>::value, ""); + static_assert(!is_copy_assignable<std::tuple<int, Poison>>::value, ""); + static_assert(!is_move_assignable<std::tuple<int, Poison>>::value, ""); + static_assert(!is_constructible<std::tuple<int, Poison>&, std::tuple<char, Poison>&>::value, ""); + static_assert(!is_assignable<std::tuple<int, Poison>&, std::tuple<char, Poison>&>::value, ""); + static_assert(!is_constructible<std::tuple<int, Poison>&, std::tuple<char, Poison>>::value, ""); + static_assert(!is_assignable<std::tuple<int, Poison>&, std::tuple<char, Poison>>::value, ""); + static_assert(!is_constructible<std::tuple<int, Poison>&, std::pair<char, Poison>&>::value, ""); + static_assert(!is_assignable<std::tuple<int, Poison>&, std::pair<char, Poison>&>::value, ""); + static_assert(!is_constructible<std::tuple<int, Poison>&, std::pair<char, Poison>>::value, ""); + static_assert(!is_assignable<std::tuple<int, Poison>&, std::pair<char, Poison>>::value, ""); +}