From 95733a55b986e73f4d8f5314e0d4557d8ae0b226 Mon Sep 17 00:00:00 2001 From: Hui Xie Date: Thu, 12 May 2022 13:23:11 +0100 Subject: [libc++] P2321R2 section [tuple.tuple]. Adding C++23 constructors, assignment operators and swaps to `tuple` 1. for constructors that takes cvref variation of tuple, there used to be two SFINAE helper _EnableCopyFromOtherTuple, _EnableMoveFromOtherTuple. And the implementations of these two helpers seem to slightly differ from the spec. But now, we need 4 variations. Instead of adding another two, this change refactored it to a single one _EnableCtrFromUTypesTuple, which directly maps to the spec without changing the C++11 behaviour. However, we need the helper __copy_cvref_t to get the type of std::get(cvref tuple) for different cvref, so I made __copy_cvref_t to be available in C++11. 2. for constructors that takes variations of std::pair, there used to be four helpers _EnableExplicitCopyFromPair, _EnableImplicitCopyFromPair, _EnableImplicitMoveFromPair, _EnableExplicitMoveFromPair. Instead of adding another four, this change refactored into two helper _EnableCtrFromPair and _BothImplicitlyConvertible. This also removes the need to use _nat 3. for const member assignment operator, since the requirement is very simple, I haven't refactored the old code but instead directly adding the new c++23 code. 4. for const swap, I pretty much copy pasted the non-const version to make these overloads look consistent 5. while doing these change, I found two of the old constructors wasn't marked constexpr for C++20 but they should. fixed them and added unit tests Reviewed By: #libc, ldionne Differential Revision: https://reviews.llvm.org/D116621 --- libcxx/include/tuple | 431 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 310 insertions(+), 121 deletions(-) (limited to 'libcxx/include/tuple') diff --git a/libcxx/include/tuple b/libcxx/include/tuple index 251b685..d0c1592 100644 --- a/libcxx/include/tuple +++ b/libcxx/include/tuple @@ -25,14 +25,24 @@ public: explicit(see-below) tuple(U&&...); // constexpr in C++14 tuple(const tuple&) = default; tuple(tuple&&) = default; + + template + constexpr explicit(see-below) tuple(tuple&); // C++23 template explicit(see-below) tuple(const tuple&); // constexpr in C++14 template explicit(see-below) tuple(tuple&&); // constexpr in C++14 + template + constexpr explicit(see-below) tuple(const tuple&&); // C++23 + + template + constexpr explicit(see-below) tuple(pair&); // iff sizeof...(Types) == 2 // C++23 template explicit(see-below) tuple(const pair&); // iff sizeof...(T) == 2 // constexpr in C++14 template explicit(see-below) tuple(pair&&); // iff sizeof...(T) == 2 // constexpr in C++14 + template + constexpr explicit(see-below) tuple(const pair&&); // iff sizeof...(Types) == 2 // C++23 // allocator-extended constructors template @@ -45,25 +55,47 @@ public: tuple(allocator_arg_t, const Alloc& a, const tuple&); // constexpr in C++20 template tuple(allocator_arg_t, const Alloc& a, tuple&&); // constexpr in C++20 + template + constexpr explicit(see-below) + tuple(allocator_arg_t, const Alloc& a, tuple&); // C++23 template explicit(see-below) tuple(allocator_arg_t, const Alloc& a, const tuple&); // constexpr in C++20 template explicit(see-below) tuple(allocator_arg_t, const Alloc& a, tuple&&); // constexpr in C++20 + template + constexpr explicit(see-below) + tuple(allocator_arg_t, const Alloc& a, const tuple&&); // C++23 + template + constexpr explicit(see-below) + tuple(allocator_arg_t, const Alloc& a, pair&); // C++23 template explicit(see-below) tuple(allocator_arg_t, const Alloc& a, const pair&); // constexpr in C++20 template explicit(see-below) tuple(allocator_arg_t, const Alloc& a, pair&&); // constexpr in C++20 + template + constexpr explicit(see-below) + tuple(allocator_arg_t, const Alloc& a, const pair&&); // C++23 tuple& operator=(const tuple&); // constexpr in C++20 + constexpr const tuple& operator=(const tuple&) const; // C++23 tuple& operator=(tuple&&) noexcept(is_nothrow_move_assignable_v && ...); // constexpr in C++20 + constexpr const tuple& operator=(tuple&&) const; // C++23 template tuple& operator=(const tuple&); // constexpr in C++20 + template + constexpr const tuple& operator=(const tuple&) const; // C++23 template tuple& operator=(tuple&&); // constexpr in C++20 + template + constexpr const tuple& operator=(tuple&&) const; // C++23 template tuple& operator=(const pair&); // iff sizeof...(T) == 2 // constexpr in C++20 + template + constexpr const tuple& operator=(const pair&) const; // iff sizeof...(Types) == 2 // C++23 template tuple& operator=(pair&&); // iff sizeof...(T) == 2 // constexpr in C++20 + template + constexpr const tuple& operator=(pair&&) const; // iff sizeof...(Types) == 2 // C++23 template tuple& operator=(array const&) // iff sizeof...(T) == N, EXTENSION @@ -71,6 +103,7 @@ public: tuple& operator=(array&&) // iff sizeof...(T) == N, EXTENSION void swap(tuple&) noexcept(AND(swap(declval(), declval())...)); // constexpr in C++20 + constexpr void swap(const tuple&) const noexcept(see-below); // C++23 }; @@ -161,6 +194,9 @@ template void swap(tuple& x, tuple& y) noexcept(noexcept(x.swap(y))); +template + constexpr void swap(const tuple& x, const tuple& y) noexcept(see-below); // C++23 + } // std */ @@ -210,6 +246,13 @@ void swap(__tuple_leaf<_Ip, _Hp, _Ep>& __x, __tuple_leaf<_Ip, _Hp, _Ep>& __y) swap(__x.get(), __y.get()); } +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 +void swap(const __tuple_leaf<_Ip, _Hp, _Ep>& __x, const __tuple_leaf<_Ip, _Hp, _Ep>& __y) + _NOEXCEPT_(__is_nothrow_swappable::value) { + swap(__x.get(), __y.get()); +} + template class __tuple_leaf { @@ -298,6 +341,12 @@ public: return 0; } + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 + int swap(const __tuple_leaf& __t) const _NOEXCEPT_(__is_nothrow_swappable::value) { + _VSTD::swap(*this, __t); + return 0; + } + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 _Hp& get() _NOEXCEPT {return __value_;} _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 const _Hp& get() const _NOEXCEPT {return __value_;} }; @@ -364,6 +413,12 @@ public: return 0; } + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 + int swap(const __tuple_leaf& __rhs) const _NOEXCEPT_(__is_nothrow_swappable::value) { + _VSTD::swap(*this, __rhs); + return 0; + } + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 _Hp& get() _NOEXCEPT {return static_cast<_Hp&>(*this);} _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 const _Hp& get() const _NOEXCEPT {return static_cast(*this);} }; @@ -454,6 +509,13 @@ struct _LIBCPP_DECLSPEC_EMPTY_BASES __tuple_impl<__tuple_indices<_Indx...>, _Tp. { _VSTD::__swallow(__tuple_leaf<_Indx, _Tp>::swap(static_cast<__tuple_leaf<_Indx, _Tp>&>(__t))...); } + + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 + void swap(const __tuple_impl& __t) const + _NOEXCEPT_(__all<__is_nothrow_swappable::value...>::value) + { + _VSTD::__swallow(__tuple_leaf<_Indx, _Tp>::swap(static_cast&>(__t))...); + } }; template @@ -689,6 +751,7 @@ public: template class _And = _And, __enable_if_t< _And...>::value , int> = 0> + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 tuple(allocator_arg_t, const _Alloc& __alloc, const tuple& __t) : __base_(allocator_arg_t(), __alloc, __t) { } @@ -696,30 +759,39 @@ public: template class _And = _And, __enable_if_t< _And...>::value , int> = 0> + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 tuple(allocator_arg_t, const _Alloc& __alloc, tuple&& __t) : __base_(allocator_arg_t(), __alloc, _VSTD::move(__t)) { } // tuple(const tuple&) constructors (including allocator_arg_t variants) - template - struct _EnableCopyFromOtherTuple : _And< - _Not, tuple<_Up...> > >, - _Lazy<_Or, - _BoolConstant, + + template , class = void> + struct _EnableCtorFromUTypesTuple : false_type {}; + + template + struct _EnableCtorFromUTypesTuple<_OtherTuple, tuple<_Up...>, + // the length of the packs needs to checked first otherwise the 2 packs cannot be expanded simultaneously below + __enable_if_t> : _And< + // the two conditions below are not in spec. The purpose is to disable the UTypes Ctor when copy/move Ctor can work. + // Otherwise, is_constructible can trigger hard error in those cases https://godbolt.org/z/M94cGdKcE + _Not >, + _Not >, + is_constructible<_Tp, __copy_cvref_t<_OtherTuple, _Up> >..., + _Lazy<_Or, _BoolConstant, // _Tp and _Up are 1-element packs - the pack expansions look // weird to avoid tripping up the type traits in degenerate cases _Lazy<_And, - _Not&, _Tp> >..., - _Not&> >... + _Not >..., + _Not >..., + _Not >... > - >, - is_constructible<_Tp, const _Up&>... - > { }; + > + > {}; template , - _EnableCopyFromOtherTuple<_Up...>, + _EnableCtorFromUTypesTuple&>, is_convertible... // explicit check >::value , int> = 0> @@ -731,8 +803,7 @@ public: template , - _EnableCopyFromOtherTuple<_Up...>, + _EnableCtorFromUTypesTuple&>, _Not<_Lazy<_And, is_convertible...> > // explicit check >::value , int> = 0> @@ -744,8 +815,7 @@ public: template , - _EnableCopyFromOtherTuple<_Up...>, + _EnableCtorFromUTypesTuple&>, is_convertible... // explicit check >::value , int> = 0> @@ -756,8 +826,7 @@ public: template , - _EnableCopyFromOtherTuple<_Up...>, + _EnableCtorFromUTypesTuple&>, _Not<_Lazy<_And, is_convertible...> > // explicit check >::value , int> = 0> @@ -766,26 +835,27 @@ public: : __base_(allocator_arg_t(), __a, __t) { } +#if _LIBCPP_STD_VER > 20 + // tuple(tuple&) constructors (including allocator_arg_t variants) + + template &>::value>* = nullptr> + _LIBCPP_HIDE_FROM_ABI constexpr + explicit(!(is_convertible_v<_Up&, _Tp> && ...)) + tuple(tuple<_Up...>& __t) : __base_(__t) {} + + template &>::value>* = nullptr> + _LIBCPP_HIDE_FROM_ABI constexpr + explicit(!(is_convertible_v<_Up&, _Tp> && ...)) + tuple(allocator_arg_t, const _Alloc& __alloc, tuple<_Up...>& __t) : __base_(allocator_arg_t(), __alloc, __t) {} +#endif // _LIBCPP_STD_VER > 20 + // tuple(tuple&&) constructors (including allocator_arg_t variants) - template - struct _EnableMoveFromOtherTuple : _And< - _Not, tuple<_Up...> > >, - _Lazy<_Or, - _BoolConstant, - // _Tp and _Up are 1-element packs - the pack expansions look - // weird to avoid tripping up the type traits in degenerate cases - _Lazy<_And, - _Not, _Tp> >..., - _Not > >... - > - >, - is_constructible<_Tp, _Up>... - > { }; template , - _EnableMoveFromOtherTuple<_Up...>, + _EnableCtorFromUTypesTuple&&>, is_convertible<_Up, _Tp>... // explicit check >::value , int> = 0> @@ -797,8 +867,7 @@ public: template , - _EnableMoveFromOtherTuple<_Up...>, + _EnableCtorFromUTypesTuple&&>, _Not<_Lazy<_And, is_convertible<_Up, _Tp>...> > // explicit check >::value , int> = 0> @@ -810,8 +879,7 @@ public: template , - _EnableMoveFromOtherTuple<_Up...>, + _EnableCtorFromUTypesTuple&&>, is_convertible<_Up, _Tp>... // explicit check >::value , int> = 0> @@ -822,8 +890,7 @@ public: template , - _EnableMoveFromOtherTuple<_Up...>, + _EnableCtorFromUTypesTuple&&>, _Not<_Lazy<_And, is_convertible<_Up, _Tp>...> > // explicit check >::value , int> = 0> @@ -832,57 +899,77 @@ public: : __base_(allocator_arg_t(), __a, _VSTD::move(__t)) { } +#if _LIBCPP_STD_VER > 20 + // tuple(const tuple&&) constructors (including allocator_arg_t variants) + + template &&>::value>* = nullptr> + _LIBCPP_HIDE_FROM_ABI constexpr + explicit(!(is_convertible_v && ...)) + tuple(const tuple<_Up...>&& __t) : __base_(std::move(__t)) {} + + template &&>::value>* = nullptr> + _LIBCPP_HIDE_FROM_ABI constexpr + explicit(!(is_convertible_v && ...)) + tuple(allocator_arg_t, const _Alloc& __alloc, const tuple<_Up...>&& __t) + : __base_(allocator_arg_t(), __alloc, std::move(__t)) {} +#endif // _LIBCPP_STD_VER > 20 + // tuple(const pair&) constructors (including allocator_arg_t variants) - template - struct _EnableImplicitCopyFromPair : _And< - is_constructible<_FirstType<_DependentTp...>, const _Up1&>, - is_constructible<_SecondType<_DependentTp...>, const _Up2&>, - is_convertible >, // explicit check - is_convertible > - > { }; - template - struct _EnableExplicitCopyFromPair : _And< - is_constructible<_FirstType<_DependentTp...>, const _Up1&>, - is_constructible<_SecondType<_DependentTp...>, const _Up2&>, - _Not > >, // explicit check - _Not > > - > { }; + template