diff --git a/libcxx/include/tuple b/libcxx/include/tuple --- a/libcxx/include/tuple +++ b/libcxx/include/tuple @@ -206,6 +206,14 @@ swap(__x.get(), __y.get()); } +#if _LIBCPP_STD_VER > 20 +template +inline _LIBCPP_HIDE_FROM_ABI constexpr +void swap(const __tuple_leaf<_Ip, _Hp, _Ep>& __lhs, const __tuple_leaf<_Ip, _Hp, _Ep>& __rhs) { + swap(__lhs.get(), __rhs.get()); +} +#endif + template class __tuple_leaf { @@ -294,6 +302,14 @@ return 0; } +#if _LIBCPP_STD_VER > 20 + _LIBCPP_HIDE_FROM_ABI constexpr + int swap(const __tuple_leaf& __rhs) const noexcept(is_nothrow_swappable_v<__tuple_leaf>) { + _VSTD::swap(*this, __rhs); + return 0; + } +#endif + _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_;} }; @@ -360,6 +376,13 @@ return 0; } +#if _LIBCPP_STD_VER > 20 + _LIBCPP_HIDE_FROM_ABI constexpr + void swap(const __tuple_leaf& __rhs) const noexcept(is_nothrow_swappable_v<__tuple_leaf>) { + _VSTD::swap(*this, __rhs); + } +#endif + _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);} }; @@ -450,6 +473,13 @@ { _VSTD::__swallow(__tuple_leaf<_Indx, _Tp>::swap(static_cast<__tuple_leaf<_Indx, _Tp>&>(__t))...); } + +#if _LIBCPP_STD_VER > 20 + _LIBCPP_HIDE_FROM_ABI constexpr + void swap(const __tuple_impl& __rhs) const noexcept(__all...>::value) { + _VSTD::__swallow(__tuple_leaf<_Indx, _Tp>::swap(static_cast&>(__rhs))...); + } +#endif }; template @@ -712,6 +742,24 @@ is_constructible<_Tp, const _Up&>... > { }; +#if _LIBCPP_STD_VER > 20 + template ::value; + }, int> = 0> + _LIBCPP_HIDE_FROM_ABI constexpr + explicit((is_convertible_v<_Up&, _Tp> && ...)) + tuple(tuple<_Up...>& __t) : __base_(__t) {} + + template ::value; + }, int> = 0> + _LIBCPP_HIDE_FROM_ABI constexpr + explicit((is_convertible_v && ...)) + tuple(allocator_arg_t, const _Alloc& __alloc, tuple<_Up...>& __t) : __base_(allocator_arg_t(), __alloc, __t) {} +#endif + template , @@ -778,6 +826,25 @@ is_constructible<_Tp, _Up>... > { }; +#if _LIBCPP_STD_VER > 20 + template ::value; + }, int> = 0> + _LIBCPP_HIDE_FROM_ABI constexpr + explicit((is_convertible_v && ...)) + tuple(const tuple<_Up...>&& __t) : __base_(_VSTD::move(__t)) {} + + template ::value; + }, int> = 0> + _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, _VSTD::move(__t)) {} +#endif + template , @@ -898,22 +965,52 @@ { } // tuple(pair&&) constructors (including allocator_arg_t variants) - template - struct _EnableImplicitMoveFromPair : _And< + template + struct _EnableMoveFromPair : _And< is_constructible<_FirstType<_DependentTp...>, _Up1>, is_constructible<_SecondType<_DependentTp...>, _Up2>, - is_convertible<_Up1, _FirstType<_DependentTp...> >, // explicit check is_convertible<_Up2, _SecondType<_DependentTp...> > + > {}; + + template + struct _EnableImplicitMoveFromPair : _And< + is_convertible<_Up1, _FirstType<_DependentTp...> >, // explicit check + _EnableMoveFromPair<_Up1, _Up2, _DependentTp...> > { }; template struct _EnableExplicitMoveFromPair : _And< - is_constructible<_FirstType<_DependentTp...>, _Up1>, - is_constructible<_SecondType<_DependentTp...>, _Up2>, _Not > >, // explicit check - _Not > > + _EnableMoveFromPair<_Up1, _Up2, _DependentTp...> > { }; +#if _LIBCPP_STD_VER > 20 + template + _LIBCPP_HIDE_FROM_ABI constexpr + explicit(requires { is_convertible_v>; + is_convertible_v>; }) + tuple(pair<_U1, _U2>& __p) : __base_(__p) {} + + template + _LIBCPP_HIDE_FROM_ABI constexpr + explicit(requires { is_convertible_v>; + is_convertible_v>; }) + tuple(allocator_arg_t, const _Alloc& __alloc, pair<_U1, _U2>& __p) : __base_(allocator_arg_t(), __alloc, __p) {} + + template + _LIBCPP_HIDE_FROM_ABI constexpr + explicit(requires { is_convertible_v>; + is_convertible_v>; }) + tuple(const pair<_U1, _U2>&& __p) : __base_(_VSTD::move(__p)) {} + + template + _LIBCPP_HIDE_FROM_ABI constexpr + explicit(requires { is_convertible_v>; + is_convertible_v>; }) + tuple(allocator_arg_t, const _Alloc& __alloc, const pair<_U1, _U2>&& __p) + : __base_(allocator_arg_t(), __alloc, _VSTD::move(__p)) {} +#endif + template class _And = _And, __enable_if_t< _And< _BoolConstant, @@ -976,6 +1073,23 @@ return *this; } +#if _LIBCPP_STD_VER > 20 + _LIBCPP_HIDE_FROM_ABI constexpr + const tuple& operator=(_If<__all...>::value, tuple, __nat> const& __tuple) const { + _VSTD::__memberwise_copy_assign(*this, __tuple, typename __make_tuple_indices::type()); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr + const tuple& operator=(_If<__all...>::value, tuple, __nat> && __tuple) const { + _VSTD::__memberwise_forward_assign(*this, + _VSTD::move(__tuple), + __tuple_types<_Tp...>(), + typename __make_tuple_indices::type()); + return *this; + } +#endif + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 tuple& operator=(_If<_And...>::value, tuple, __nat>&& __tuple) _NOEXCEPT_((_And...>::value)) @@ -1017,6 +1131,33 @@ return *this; } + +#if _LIBCPP_STD_VER > 20 + template &, const _U1&>; + is_assignable_v&, const _U2&>; + }, int> = 0> + _LIBCPP_HIDE_FROM_ABI constexpr + const tuple& operator=(const pair<_U1, _U2>& __pair) const { + _VSTD::get<0>(*this) = __pair.first; + _VSTD::get<1>(*this) = __pair.second; + return *this; + } + + template &, const _U1&>; + is_assignable_v&, const _U2&>; + }, int> = 0> + _LIBCPP_HIDE_FROM_ABI constexpr + const tuple& operator=(pair<_U1, _U2>&& __pair) const { + _VSTD::get<0>(*this) = _VSTD::forward<_U1>(__pair.first); + _VSTD::get<1>(*this) = _VSTD::forward<_U2>(__pair.second); + return *this; + } +#endif + template, @@ -1092,6 +1233,13 @@ _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 void swap(tuple& __t) _NOEXCEPT_(__all<__is_nothrow_swappable<_Tp>::value...>::value) {__base_.swap(__t.__base_);} + +#if _LIBCPP_STD_VER > 20 + _LIBCPP_HIDE_FROM_ABI constexpr + void swap(const tuple& __rhs) const noexcept(__all...>::value) { + __base_.swap(__rhs.__base_); + } +#endif }; template <> @@ -1114,6 +1262,9 @@ tuple(allocator_arg_t, const _Alloc&, array<_Up, 0>) _NOEXCEPT {} _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 void swap(tuple&) _NOEXCEPT {} +#if _LIBCPP_STD_VER > 20 + _LIBCPP_HIDE_FROM_ABI constexpr void swap(const tuple&) const noexcept {} +#endif }; #if _LIBCPP_STD_VER > 20 @@ -1154,6 +1305,16 @@ _NOEXCEPT_(__all<__is_nothrow_swappable<_Tp>::value...>::value) {__t.swap(__u);} +#if _LIBCPP_STD_VER > 20 +template +_LIBCPP_HIDE_FROM_ABI constexpr +enable_if_t<__all...>::value, void> +swap(const tuple<_Tp...>& __lhs, const tuple<_Tp...>& __rhs) + noexcept(__all...>::value) { + __lhs.swap(__rhs); +} +#endif + // get template diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/assign.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/assign.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/assign.pass.cpp @@ -0,0 +1,147 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// template class tuple; + +// template tuple(const tuple& u); + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +#include +#include + +struct MutableCopy { + int val; + + constexpr MutableCopy() = default; + constexpr MutableCopy(int _val) : val(_val) {} + constexpr MutableCopy& operator=(const MutableCopy& o) { val = o.val; return *this; } +}; + +struct ConstCopy { + mutable int val; + + constexpr ConstCopy() = default; + constexpr ConstCopy(int _val) : val(_val) {} + constexpr const ConstCopy& operator=(const ConstCopy& o) const { val = o.val; return *this; } +}; + +struct MutableMove { + int val; + + constexpr MutableMove() = default; + constexpr MutableMove(int _val) : val(_val) {} + constexpr MutableMove& operator=(MutableMove&& o) { val = o.val; return *this; } +}; + +struct ConstMove { + mutable int val; + + constexpr ConstMove() = default; + constexpr ConstMove(int _val) : val(_val) {} + constexpr const ConstMove& operator=(const ConstMove&& o) const { val = o.val; return *this; } + constexpr ConstMove& operator=(const ConstMove&) = delete; +}; + +template +struct ConvertibleFrom { + T v; + constexpr ConvertibleFrom() = default; + constexpr ConvertibleFrom(T& _v) : v(_v) {} + constexpr ConvertibleFrom(T&& _v) : v(std::move(_v)) {} +}; + +template +struct ConstConvertibleFrom { + mutable T v; + constexpr ConstConvertibleFrom() = default; + constexpr ConstConvertibleFrom(T& _v) : v(_v) {} + constexpr ConstConvertibleFrom(T&& _v) : v(std::move(_v)) {} + constexpr const ConstConvertibleFrom& operator=(const ConstConvertibleFrom& o) const { v = o.v; return *this; } +}; + +constexpr bool test() { + { + using T = std::tuple; + + T t1{1}; + T t2; + t2 = t1; + assert(std::get<0>(t2).val == 1); + } + { + using T = std::tuple; + + T t1{1}; + const T t2; + t2 = t1; + assert(std::get<0>(t2).val == 1); + } + { + using T = std::tuple; + + T t1{1}; + T t2; + t2 = std::move(t1); + assert(std::get<0>(t2).val == 1); + } + { + using T = std::tuple; + + T t1{1}; + const T t2; + t2 = std::move(t1); + assert(std::get<0>(t2).val == 1); + } + + { + using T1 = std::pair; + using T2 = std::tuple; + + T1 t1{1, 2}; + T2 t2; + t2 = t1; + assert(std::get<0>(t2).val == 1); + } + { + using T1 = std::pair>; + using T2 = std::tuple>; + + T1 t1{1, 2}; + const T2 t2; + t2 = t1; + assert(std::get<0>(t2).val == 1); + } + { + using T1 = std::pair; + using T2 = std::tuple; + + T1 t1{1, 2}; + T2 t2; + t2 = std::move(t1); + assert(std::get<0>(t2).val == 1); + } + { + using T1 = std::pair>; + using T2 = std::tuple>; + + T1 t1{1, 2}; + const T2 t2; + t2 = std::move(t1); + assert(std::get<0>(t2).val == 1); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); +} diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_cpp23.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_cpp23.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_cpp23.pass.cpp @@ -0,0 +1,141 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// template class tuple; + +// template tuple(const tuple& u); + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +#include +#include + +struct MutableCopy { + int val; + + constexpr MutableCopy() = default; + constexpr MutableCopy(int _val) : val(_val) {} + constexpr MutableCopy(MutableCopy& o) : val(o.val) {} + constexpr MutableCopy(const MutableCopy&) = delete; +}; + +struct ConstCopy { + mutable int val; + + constexpr ConstCopy() = default; + constexpr ConstCopy(int _val) : val(_val) {} + constexpr ConstCopy(const ConstCopy& o) : val(o.val) {} + constexpr ConstCopy(ConstCopy&) = delete; +}; + +struct MutableMove { + int val; + + constexpr MutableMove() = default; + constexpr MutableMove(int _val) : val(_val) {} + constexpr MutableMove(MutableMove&& o) : val(o.val) {} + constexpr MutableMove(const MutableMove&&) = delete; +}; + +struct ConstMove { + mutable int val; + + constexpr ConstMove() = default; + constexpr ConstMove(int _val) : val(_val) {} + constexpr ConstMove(const ConstMove&& o) : val(o.val) {} + constexpr ConstMove(ConstMove&&) = delete; +}; + +template +struct ConvertibleFrom { + T v; + constexpr ConvertibleFrom() = default; + constexpr ConvertibleFrom(T& _v) : v(_v) {} + constexpr ConvertibleFrom(T&& _v) : v(std::move(_v)) {} +}; + +constexpr bool test() { + { + using T1 = std::tuple; + using T2 = std::tuple>; + + T1 t1{1}; + T2 t2{t1}; + assert(std::get<0>(t2).v.val == 1); + } + { + using T1 = std::tuple; + using T2 = std::tuple>; + + const T1 t1{1}; + T2 t2{t1}; + assert(std::get<0>(t2).v.val == 1); + } + { + using T1 = std::tuple; + using T2 = std::tuple>; + + T1 t1{1}; + T2 t2{std::move(t1)}; + assert(std::get<0>(t2).v.val == 1); + } + { + using T1 = std::tuple; + using T2 = std::tuple>; + + const T1 t1{1}; + T2 t2{std::move(t1)}; + assert(std::get<0>(t2).v.val == 1); + } + + { + using T1 = std::pair; + using T2 = std::tuple, int>; + + const T1 t1{1, 2}; + T2 t2{std::move(t1)}; + assert(std::get<0>(t2).v.val == 1); + assert(std::get<1>(t2) == 2); + } + { + using T1 = std::pair; + using T2 = std::tuple, int>; + + const T1 t1{1, 2}; + T2 t2{t1}; + assert(std::get<0>(t2).v.val == 1); + assert(std::get<1>(t2) == 2); + } + { + using T1 = std::pair; + using T2 = std::tuple, int>; + + T1 t1{1, 2}; + T2 t2{std::move(t1)}; + assert(std::get<0>(t2).v.val == 1); + assert(std::get<1>(t2) == 2); + } + { + using T1 = std::pair; + using T2 = std::tuple, int>; + + const T1 t1{1, 2}; + T2 t2{std::move(t1)}; + assert(std::get<0>(t2).v.val == 1); + assert(std::get<1>(t2) == 2); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.special/non_member_swap_const.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.special/non_member_swap_const.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.special/non_member_swap_const.pass.cpp @@ -0,0 +1,61 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// template class tuple; + +// template +// void swap(const tuple& x, const tuple& y); + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +#include +#include + +struct S { + int* calls; + friend constexpr void swap(S& a, S& b) { + *a.calls += 1; + *b.calls += 1; + } +}; +struct CS { + int* calls; + friend constexpr void swap(const CS& a, const CS& b) { + *a.calls += 1; + *b.calls += 1; + } +}; + +static_assert(std::is_swappable_v>); +static_assert(std::is_swappable_v>); +static_assert(std::is_swappable_v>); +static_assert(std::is_swappable_v>); +static_assert(std::is_swappable_v>); +static_assert(std::is_swappable_v>); +static_assert(std::is_swappable_v>); +static_assert(!std::is_swappable_v>); +static_assert(std::is_swappable_v>); +static_assert(std::is_swappable_v>); +static_assert(!std::is_swappable_v>); +static_assert(std::is_swappable_v>); + +int main(int, char**) { + int cs_calls = 0; + int s_calls = 0; + S s1{&s_calls}; + S s2{&s_calls}; + const std::tuple t1 = {CS{&cs_calls}, s1}; + const std::tuple t2 = {CS{&cs_calls}, s2}; + swap(t1, t2); + assert(cs_calls == 1); + assert(s_calls == 1); + + return 0; +} diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.swap/member_swap_const.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.swap/member_swap_const.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.swap/member_swap_const.pass.cpp @@ -0,0 +1,62 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// void swap(const tuple& rhs); + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +#include +#include + +struct ConstSwappable { + mutable int i; +}; + +constexpr void swap(const ConstSwappable& lhs, const ConstSwappable& rhs) { std::swap(lhs.i, rhs.i); } + +constexpr bool test() { + { + typedef std::tuple T; + const T t0({0}); + T t1({1}); + t0.swap(t1); + assert(std::get<0>(t0).i == 1); + assert(std::get<0>(t1).i == 0); + } + { + typedef std::tuple T; + const T t0({0}, {1}); + const T t1({2}, {3}); + t0.swap(t1); + assert(std::get<0>(t0).i == 2); + assert(std::get<1>(t0).i == 3); + assert(std::get<0>(t1).i == 0); + assert(std::get<1>(t1).i == 1); + } + { + typedef std::tuple T; + const T t0({0}, {1}, {2}); + const T t1({3}, {4}, {5}); + t0.swap(t1); + assert(std::get<0>(t0).i == 3); + assert(std::get<1>(t0).i == 4); + assert(std::get<2>(t0).i == 5); + assert(std::get<0>(t1).i == 0); + assert(std::get<1>(t1).i == 1); + assert(std::get<2>(t1).i == 2); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +}