diff --git a/libcxx/docs/Status/ZipProjects.csv b/libcxx/docs/Status/ZipProjects.csv --- a/libcxx/docs/Status/ZipProjects.csv +++ b/libcxx/docs/Status/ZipProjects.csv @@ -1,27 +1,27 @@ Section,Description,Dependencies,Assignee,Complete | `[tuple.syn] `_, "`[tuple] basic_common_reference, common_type `_", None, Nikolas Klauser, |Complete| -| `[tuple.tuple] `_, "`[tuple] constructor, assignment and swap overloads `_", None, Nikolas Klauser, |In Progress| +| `[tuple.tuple] `_, "`[tuple] constructor, assignment and swap overloads `_", None, Hui Xie, |Complete| | `[utility.syn] `_, "[pair] basic_common_reference, common_type", None, Nikolas Klauser, |Complete| -| `[pairs.pair] `_, "[pair] constructor, assignment and swap overloads", None, Nikolas Klauser, |Not Started| +| `[pairs.pair] `_, "`[pair] constructor, assignment and swap overloads `_", None, Hui Xie, |Complete| "| `[memory.syn] `_ | `[allocator.uses.construction] `_", "[pair] uses_allocator_construction_args overloads", None, Unassigned, |Not Started| -| `[vector.bool] `_, "[vector::reference] add const operator= overload", None, Nikolas Klauser, |Not Started| -| `[iterator.concept.winc] `_, "Update weakly_comparable", None, Unassigned, |Not Started| +| `[vector.bool] `_, "[vector::reference] add const operator= overload", None, Hui Xie, |Not Started| +| `[iterator.concept.winc] `_, "Update weakly_comparable", None, Hui Xie, |Not Started| | `[range.zip] `_, "`zip_view `_", "| `zip_view::iterator` | `zip_view::sentinel`", Hui Xie, |Complete| | `[range.zip.iterator] `_, "`zip_view::iterator `_", None, Hui Xie, |Complete| | `[range.zip.sentinel] `_, "`zip_view::sentinel `_", None, Hui Xie, |Complete| | `[range.zip.transform.view] `_, "zip_transform_view", "| `zip_transform_view::iterator` -| `zip_transform_view::sentinel`", Unassigned, |Not Started| -| `[range.zip.transform.iterator] `_, "zip_transform_view::iterator", None, Unassigned, |Not Started| -| `[range.zip.transform.sentinel] `_, "zip_transform_view::sentinel", None, Unassigned, |Not Started| +| `zip_transform_view::sentinel`", Hui Xie, |Not Started| +| `[range.zip.transform.iterator] `_, "zip_transform_view::iterator", None, Hui Xie, |Not Started| +| `[range.zip.transform.sentinel] `_, "zip_transform_view::sentinel", None, Hui Xie, |Not Started| | `[range.adjacent.view] `_, "adjacent_view", "| `adjacent_view::iterator` -| `adjacent_view::sentinel`", Unassigned, |Not Started| -| `[range.adjacent.iterator] `_, "adjacent_view::iterator", None, Unassigned, |Not Started| -| `[range.adjacent.sentinel] `_, "adjacent_view::sentinel", None, Unassigned, |Not Started| +| `adjacent_view::sentinel`", Hui Xie, |Not Started| +| `[range.adjacent.iterator] `_, "adjacent_view::iterator", None, unassigned, |Not Started| +| `[range.adjacent.sentinel] `_, "adjacent_view::sentinel", None, unassigned, |Not Started| | `[range.adjacent.transform.view] `_, "adjacent_transform_view", "| `adjacent_transform_view::iterator`, -| `adjacent_transform_view::sentinel`", Unassigned, |Not Started| -| `[range.adjacent.transform.iterator] `_, "adjacent_transform_view::iterator", None, Unassigned, |Not Started| -| `[range.adjacent.transform.sentinel] `_, "adjacent_transform_view::sentinel", None, Unassigned, |Not Started| +| `adjacent_transform_view::sentinel`", Hui Xie, |Not Started| +| `[range.adjacent.transform.iterator] `_, "adjacent_transform_view::iterator", None, Hui Xie, |Not Started| +| `[range.adjacent.transform.sentinel] `_, "adjacent_transform_view::sentinel", None, Hui Xie, |Not Started| | `[ranges.syn] `_, "enable_borrowed_range zip_view and adjacent_view", "| `zip_view` -| `adjacent_view`", Unassigned, |Not Started| +| `adjacent_view`", Hui Xie, |Not Started| diff --git a/libcxx/include/__utility/pair.h b/libcxx/include/__utility/pair.h --- a/libcxx/include/__utility/pair.h +++ b/libcxx/include/__utility/pair.h @@ -90,20 +90,26 @@ } template - static constexpr bool __enable_explicit() { + static constexpr bool __is_pair_constructible() { return is_constructible::value - && is_constructible::value - && (!is_convertible<_U1, first_type>::value - || !is_convertible<_U2, second_type>::value); + && is_constructible::value; } template - static constexpr bool __enable_implicit() { - return is_constructible::value - && is_constructible::value - && is_convertible<_U1, first_type>::value + static constexpr bool __is_implicit() { + return is_convertible<_U1, first_type>::value && is_convertible<_U2, second_type>::value; } + + template + static constexpr bool __enable_explicit() { + return __is_pair_constructible<_U1, _U2>() && !__is_implicit<_U1, _U2>(); + } + + template + static constexpr bool __enable_implicit() { + return __is_pair_constructible<_U1, _U2>() && __is_implicit<_U1, _U2>(); + } }; template @@ -198,6 +204,17 @@ is_nothrow_constructible::value)) : first(_VSTD::forward<_U1>(__u1)), second(_VSTD::forward<_U2>(__u2)) {} +#if _LIBCPP_STD_VER > 20 + template() + >* = nullptr> + _LIBCPP_HIDE_FROM_ABI constexpr + explicit(!_CheckArgs::template __is_implicit<_U1&, _U2&>()) pair(pair<_U1, _U2>& __p) + noexcept((is_nothrow_constructible::value && + is_nothrow_constructible::value)) + : first(__p.first), second(__p.second) {} +#endif + template() >::type* = nullptr> @@ -234,6 +251,18 @@ is_nothrow_constructible::value)) : first(_VSTD::forward<_U1>(__p.first)), second(_VSTD::forward<_U2>(__p.second)) {} +#if _LIBCPP_STD_VER > 20 + template() + >* = nullptr> + _LIBCPP_HIDE_FROM_ABI constexpr + explicit(!_CheckArgs::template __is_implicit()) + pair(const pair<_U1, _U2>&& __p) + noexcept(is_nothrow_constructible::value && + is_nothrow_constructible::value) + : first(std::move(__p.first)), second(std::move(__p.second)) {} +#endif + template::template __enable_explicit<_Tuple>() >::type* = nullptr> @@ -286,6 +315,50 @@ return *this; } +#if _LIBCPP_STD_VER > 20 + _LIBCPP_HIDE_FROM_ABI constexpr + const pair& operator=(pair const& __p) const + noexcept(is_nothrow_copy_assignable_v && + is_nothrow_copy_assignable_v) + requires(is_copy_assignable_v && + is_copy_assignable_v) { + first = __p.first; + second = __p.second; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr + const pair& operator=(pair&& __p) const + noexcept(is_nothrow_assignable_v && + is_nothrow_assignable_v) + requires(is_assignable_v && + is_assignable_v) { + first = std::forward(__p.first); + second = std::forward(__p.second); + return *this; + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr + const pair& operator=(const pair<_U1, _U2>& __p) const + requires(is_assignable_v && + is_assignable_v) { + first = __p.first; + second = __p.second; + return *this; + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr + const pair& operator=(pair<_U1, _U2>&& __p) const + requires(is_assignable_v && + is_assignable_v) { + first = std::forward<_U1>(__p.first); + second = std::forward<_U2>(__p.second); + return *this; + } +#endif // _LIBCPP_STD_VER > 20 + template ::template __enable_assign<_Tuple>() >::type* = nullptr> @@ -306,6 +379,18 @@ swap(first, __p.first); swap(second, __p.second); } + +#if _LIBCPP_STD_VER > 20 + _LIBCPP_HIDE_FROM_ABI constexpr + void swap(const pair& __p) const + noexcept(__is_nothrow_swappable::value && + __is_nothrow_swappable::value) + { + using std::swap; + swap(first, __p.first); + swap(second, __p.second); + } +#endif private: #ifndef _LIBCPP_CXX03_LANG @@ -422,6 +507,18 @@ __x.swap(__y); } +#if _LIBCPP_STD_VER > 20 +template + requires (__is_swappable::value && + __is_swappable::value) +_LIBCPP_HIDE_FROM_ABI constexpr +void swap(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y) + noexcept(noexcept(__x.swap(__y))) +{ + __x.swap(__y); +} +#endif + template inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX14 pair::type, typename __unwrap_ref_decay<_T2>::type> diff --git a/libcxx/include/utility b/libcxx/include/utility --- a/libcxx/include/utility +++ b/libcxx/include/utility @@ -84,19 +84,29 @@ explicit(see-below) constexpr pair(); explicit(see-below) pair(const T1& x, const T2& y); // constexpr in C++14 template explicit(see-below) pair(U&&, V&&); // constexpr in C++14 + template constexpr explicit(see below) pair(pair&); // since C++23 template explicit(see-below) pair(const pair& p); // constexpr in C++14 template explicit(see-below) pair(pair&& p); // constexpr in C++14 + template + constexpr explicit(see below) pair(const pair&&); // since C++23 template pair(piecewise_construct_t, tuple first_args, tuple second_args); // constexpr in C++20 + constexpr const pair& operator=(const pair& p) const; // since C++23 template pair& operator=(const pair& p); // constexpr in C++20 + template + constexpr const pair& operator=(const pair& p) const; // since C++23 pair& operator=(pair&& p) noexcept(is_nothrow_move_assignable::value && is_nothrow_move_assignable::value); // constexpr in C++20 + constexpr const pair& operator=(pair&& p) const; // since C++23 template pair& operator=(pair&& p); // constexpr in C++20 + template + constexpr const pair& operator=(pair&& p) const; // since C++23 void swap(pair& p) noexcept(is_nothrow_swappable_v && is_nothrow_swappable_v); // constexpr in C++20 + constexpr void swap(const pair& p) const noexcept(see below); // since C++23 }; template class TQual, template class UQual> @@ -123,6 +133,9 @@ void swap(pair& x, pair& y) noexcept(noexcept(x.swap(y))); // constexpr in C++20 +template +constexpr void swap(const pair& x, const pair& y) noexcept(noexcept(x.swap(y))); // since C++23 + struct piecewise_construct_t { explicit piecewise_construct_t() = default; }; inline constexpr piecewise_construct_t piecewise_construct = piecewise_construct_t(); diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_convert_copy.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_convert_copy.pass.cpp --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_convert_copy.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_convert_copy.pass.cpp @@ -22,7 +22,7 @@ #include #include "test_macros.h" -#include "types.h" +#include "copy_move_types.h" // test constraints diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_convert_move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_convert_move.pass.cpp --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_convert_move.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_convert_move.pass.cpp @@ -22,7 +22,7 @@ #include #include "test_macros.h" -#include "types.h" +#include "copy_move_types.h" // test constraints diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_copy.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_copy.pass.cpp --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_copy.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_copy.pass.cpp @@ -21,7 +21,7 @@ #include #include "test_macros.h" -#include "types.h" +#include "copy_move_types.h" static_assert(!std::is_assignable_v&, const std::tuple&>); static_assert(std::is_assignable_v&, const std::tuple&>); diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_move.pass.cpp --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_move.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_move.pass.cpp @@ -21,7 +21,7 @@ #include #include "test_macros.h" -#include "types.h" +#include "copy_move_types.h" static_assert(!std::is_assignable_v&, std::tuple&&>); static_assert(std::is_assignable_v&, std::tuple&&>); diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair_copy.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair_copy.pass.cpp --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair_copy.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair_copy.pass.cpp @@ -23,7 +23,7 @@ #include #include "test_macros.h" -#include "types.h" +#include "copy_move_types.h" // test constraints diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair_move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair_move.pass.cpp --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair_move.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair_move.pass.cpp @@ -23,7 +23,7 @@ #include #include "test_macros.h" -#include "types.h" +#include "copy_move_types.h" // test constraints diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/types.h b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/types.h deleted file mode 100644 --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/types.h +++ /dev/null @@ -1,138 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// -#ifndef LIBCXX_TEST_STD_UTILITIES_TUPLE_ASSIGN_TYPES_H -#define LIBCXX_TEST_STD_UTILITIES_TUPLE_ASSIGN_TYPES_H - -#include "test_allocator.h" -#include - -struct CopyAssign { - int val; - - constexpr CopyAssign() = default; - constexpr CopyAssign(int v) : val(v) {} - - constexpr CopyAssign& operator=(const CopyAssign&) = default; - - constexpr const CopyAssign& operator=(const CopyAssign&) const = delete; - constexpr CopyAssign& operator=(CopyAssign&&) = delete; - constexpr const CopyAssign& operator=(CopyAssign&&) const = delete; -}; - -struct ConstCopyAssign { - mutable int val; - - constexpr ConstCopyAssign() = default; - constexpr ConstCopyAssign(int v) : val(v) {} - - constexpr const ConstCopyAssign& operator=(const ConstCopyAssign& other) const { - val = other.val; - return *this; - } - - constexpr ConstCopyAssign& operator=(const ConstCopyAssign&) = delete; - constexpr ConstCopyAssign& operator=(ConstCopyAssign&&) = delete; - constexpr const ConstCopyAssign& operator=(ConstCopyAssign&&) const = delete; -}; - -struct MoveAssign { - int val; - - constexpr MoveAssign() = default; - constexpr MoveAssign(int v) : val(v) {} - - constexpr MoveAssign& operator=(MoveAssign&&) = default; - - constexpr MoveAssign& operator=(const MoveAssign&) = delete; - constexpr const MoveAssign& operator=(const MoveAssign&) const = delete; - constexpr const MoveAssign& operator=(MoveAssign&&) const = delete; -}; - -struct ConstMoveAssign { - mutable int val; - - constexpr ConstMoveAssign() = default; - constexpr ConstMoveAssign(int v) : val(v) {} - - constexpr const ConstMoveAssign& operator=(ConstMoveAssign&& other) const { - val = other.val; - return *this; - } - - constexpr ConstMoveAssign& operator=(const ConstMoveAssign&) = delete; - constexpr const ConstMoveAssign& operator=(const ConstMoveAssign&) const = delete; - constexpr ConstMoveAssign& operator=(ConstMoveAssign&&) = delete; -}; - -template -struct AssignableFrom { - T v; - - constexpr AssignableFrom() = default; - - template - constexpr AssignableFrom(U&& u) - requires std::is_constructible_v - : v(std::forward(u)) {} - - constexpr AssignableFrom& operator=(const T& t) - requires std::is_copy_assignable_v - { - v = t; - return *this; - } - - constexpr AssignableFrom& operator=(T&& t) - requires std::is_move_assignable_v - { - v = std::move(t); - return *this; - } - - constexpr const AssignableFrom& operator=(const T& t) const - requires std::is_assignable_v - { - v = t; - return *this; - } - - constexpr const AssignableFrom& operator=(T&& t) const - requires std::is_assignable_v - { - v = std::move(t); - return *this; - } -}; - -struct TracedAssignment { - int copyAssign = 0; - mutable int constCopyAssign = 0; - int moveAssign = 0; - mutable int constMoveAssign = 0; - - constexpr TracedAssignment() = default; - - constexpr TracedAssignment& operator=(const TracedAssignment&) { - copyAssign++; - return *this; - } - constexpr const TracedAssignment& operator=(const TracedAssignment&) const { - constCopyAssign++; - return *this; - } - constexpr TracedAssignment& operator=(TracedAssignment&&) { - moveAssign++; - return *this; - } - constexpr const TracedAssignment& operator=(TracedAssignment&&) const { - constMoveAssign++; - return *this; - } -}; - -#endif diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_move_pair.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_move_pair.pass.cpp --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_move_pair.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_move_pair.pass.cpp @@ -25,7 +25,7 @@ #include #include -#include "convert_types.h" +#include "copy_move_types.h" #include "test_allocator.h" // test constraints diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_const_move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_const_move.pass.cpp --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_const_move.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_const_move.pass.cpp @@ -31,7 +31,7 @@ #include #include -#include "convert_types.h" +#include "copy_move_types.h" #include "test_allocator.h" #include "test_macros.h" diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_non_const_copy.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_non_const_copy.pass.cpp --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_non_const_copy.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_non_const_copy.pass.cpp @@ -31,7 +31,7 @@ #include #include -#include "convert_types.h" +#include "copy_move_types.h" #include "test_allocator.h" #include "test_macros.h" diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_non_const_pair.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_non_const_pair.pass.cpp --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_non_const_pair.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_non_const_pair.pass.cpp @@ -24,7 +24,7 @@ #include #include -#include "convert_types.h" +#include "copy_move_types.h" #include "test_allocator.h" // test constraints diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_move_pair.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_move_pair.pass.cpp --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_move_pair.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_move_pair.pass.cpp @@ -23,7 +23,7 @@ #include #include -#include "convert_types.h" +#include "copy_move_types.h" // test constraints // sizeof...(Types) == 2 diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp @@ -30,7 +30,7 @@ #include #include -#include "convert_types.h" +#include "copy_move_types.h" #include "test_macros.h" // test: The expression inside explicit is equivalent to: diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_non_const_copy.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_non_const_copy.pass.cpp --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_non_const_copy.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_non_const_copy.pass.cpp @@ -29,7 +29,7 @@ #include #include -#include "convert_types.h" +#include "copy_move_types.h" #include "test_macros.h" // test: The expression inside explicit is equivalent to: diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/non_const_pair.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/non_const_pair.pass.cpp --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/non_const_pair.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/non_const_pair.pass.cpp @@ -23,7 +23,7 @@ #include #include -#include "convert_types.h" +#include "copy_move_types.h" // test constraints // sizeof...(Types) == 2 diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_copy_convert.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_copy_convert.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_copy_convert.pass.cpp @@ -0,0 +1,87 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// template struct pair +// template constexpr +// const pair& operator=(const pair& p) const; + +#include +#include + +#include "test_macros.h" +#include "copy_move_types.h" + +// Constraints: +// is_assignable_v is true, and +// is_assignable_v is true. + +// clang-format off +static_assert( std::is_assignable_v&, + const std::pair&>); +static_assert(!std::is_assignable_v&, + const std::pair&>); +static_assert(!std::is_assignable_v&, + const std::pair&>); +static_assert(!std::is_assignable_v&, + const std::pair&>); + +static_assert(std::is_assignable_v< + const std::pair, AssignableFrom>&, + const std::pair&>); + +static_assert(!std::is_assignable_v< + const std::pair, AssignableFrom>&, + const std::pair&>); +// clang-format on + +constexpr bool test() { + // reference types + { + int i1 = 1; + int i2 = 2; + long j1 = 3; + long j2 = 4; + const std::pair p1{i1, i2}; + const std::pair p2{j1, j2}; + p2 = p1; + assert(p2.first == 1); + assert(p2.second == 2); + } + + // user defined const copy assignment + { + const std::pair p1{1, 2}; + const std::pair, AssignableFrom> p2{3, 4}; + p2 = p1; + assert(p2.first.v.val == 1); + assert(p2.second.v.val == 2); + } + + // The correct assignment operator of the underlying type is used + { + std::pair t1{}; + const std::pair, AssignableFrom> t2{}; + t2 = t1; + assert(t2.first.v.constCopyAssign == 1); + assert(t2.second.v.constCopyAssign == 1); + } + + return true; +} + +int main(int, const char**) { + test(); +// gcc cannot have mutable member in constant expression +#if !defined(TEST_COMPILER_GCC) + static_assert(test()); +#endif +} diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_copy_pair.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_copy_pair.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_copy_pair.pass.cpp @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// template struct pair +// constexpr const pair& operator=(const pair& p) const; + +#include +#include + +#include "test_macros.h" +#include "copy_move_types.h" + +// Constraints: +// is_copy_assignable is true and +// is_copy_assignable is true. + +// clang-format off +static_assert(std::is_assignable_v&, + const std::pair&>); +static_assert(!std::is_assignable_v&, + const std::pair&>); +static_assert(!std::is_assignable_v&, + const std::pair&>); +static_assert(!std::is_assignable_v&, + const std::pair&>); + +static_assert(std::is_assignable_v&, + const std::pair&>); +static_assert(!std::is_assignable_v&, + const std::pair&>); + +// clang-format on + +constexpr bool test() { + // reference types + { + int i1 = 1; + int i2 = 2; + double d1 = 3.0; + double d2 = 5.0; + const std::pair p1{i1, d1}; + const std::pair p2{i2, d2}; + p2 = p1; + assert(p2.first == 1); + assert(p2.second == 3.0); + } + + // user defined const copy assignment + { + const std::pair p1{1, 2}; + const std::pair p2{3, 4}; + p2 = p1; + assert(p2.first.val == 1); + assert(p2.second.val == 2); + } + + // The correct assignment operator of the underlying type is used + { + std::pair t1{}; + const std::pair t2{}; + t2 = t1; + assert(t2.first.constCopyAssign == 1); + assert(t2.second.constCopyAssign == 1); + } + + return true; +} + +int main(int, const char**) { + test(); +// gcc cannot have mutable member in constant expression +#if !defined(TEST_COMPILER_GCC) + static_assert(test()); +#endif +} diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_move_convert.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_move_convert.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_move_convert.pass.cpp @@ -0,0 +1,87 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// template struct pair +// template +// constexpr const pair& operator=(pair&& p) const; + +#include +#include + +#include "test_macros.h" +#include "copy_move_types.h" + +// Constraints: +// is_assignable is true and +// is_assignable is true. + +// clang-format off +static_assert( std::is_assignable_v&, + std::pair&&>); +static_assert(!std::is_assignable_v&, + std::pair&&>); +static_assert(!std::is_assignable_v&, + std::pair&&>); +static_assert(!std::is_assignable_v&, + std::pair&&>); + +static_assert(std::is_assignable_v< + const std::pair, AssignableFrom>&, + std::pair&&>); + +static_assert(!std::is_assignable_v< + const std::pair, AssignableFrom>&, + std::pair&&>); +// clang-format on + +constexpr bool test() { + // reference types + { + int i1 = 1; + int i2 = 2; + long j1 = 3; + long j2 = 4; + std::pair p1{std::move(i1), std::move(i2)}; + const std::pair p2{std::move(j1), std::move(j2)}; + p2 = std::move(p1); + assert(p2.first == 1); + assert(p2.second == 2); + } + + // user defined const move assignment + { + std::pair p1{1, 2}; + const std::pair, AssignableFrom> p2{3, 4}; + p2 = std::move(p1); + assert(p2.first.v.val == 1); + assert(p2.second.v.val == 2); + } + + // The correct assignment operator of the underlying type is used + { + std::pair t1{}; + const std::pair, AssignableFrom> t2{}; + t2 = std::move(t1); + assert(t2.first.v.constMoveAssign == 1); + assert(t2.second.v.constMoveAssign == 1); + } + + return true; +} + +int main(int, const char**) { + test(); +// gcc cannot have mutable member in constant expression +#if !defined(TEST_COMPILER_GCC) + static_assert(test()); +#endif +} diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_move_pair.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_move_pair.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_move_pair.pass.cpp @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// template struct pair +// constexpr const pair& operator=(pair&& p) const; + +#include +#include + +#include "test_macros.h" +#include "copy_move_types.h" + +// Constraints: +// is_assignable is true and +// is_assignable is true. + +// clang-format off +static_assert(std::is_assignable_v&, + std::pair&&>); +static_assert(!std::is_assignable_v&, + std::pair&&>); +static_assert(!std::is_assignable_v&, + std::pair&&>); +static_assert(!std::is_assignable_v&, + std::pair&&>); + +static_assert(std::is_assignable_v&, + std::pair&&>); +static_assert(!std::is_assignable_v&, + std::pair&&>); + +// clang-format on + +constexpr bool test() { + // reference types + { + int i1 = 1; + int i2 = 2; + double d1 = 3.0; + double d2 = 5.0; + std::pair p1{std::move(i1), std::move(d1)}; + const std::pair p2{std::move(i2), std::move(d2)}; + p2 = std::move(p1); + assert(p2.first == 1); + assert(p2.second == 3.0); + } + + // user defined const move assignment + { + std::pair p1{1, 2}; + const std::pair p2{3, 4}; + p2 = std::move(p1); + assert(p2.first.val == 1); + assert(p2.second.val == 2); + } + + // The correct assignment operator of the underlying type is used + { + std::pair t1{}; + const std::pair t2{}; + t2 = std::move(t1); + assert(t2.first.constMoveAssign == 1); + assert(t2.second.constCopyAssign == 1); + } + + return true; +} + +int main(int, const char**) { + test(); +// gcc cannot have mutable member in constant expression +#if !defined(TEST_COMPILER_GCC) + static_assert(test()); +#endif +} diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor_pair_U_V_const_move.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor_pair_U_V_const_move.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor_pair_U_V_const_move.pass.cpp @@ -0,0 +1,89 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// template struct pair +// template +// constexpr explicit(see below) pair(const pair&& p); + +#include +#include + +#include "copy_move_types.h" +#include "test_macros.h" + +// Constraints: +// is_constructible_v(FWD(p)))> is true and +// is_constructible_v(FWD(p)))> is true. +struct X {}; +struct Y {}; +struct NotConvertibleToXorY {}; + +static_assert(std::is_constructible_v, const std::pair&&>); +static_assert(!std::is_constructible_v, const std::pair&&>); +static_assert(!std::is_constructible_v, const std::pair&&>); +static_assert(!std::is_constructible_v, const std::pair&&>); + +// The expression inside explicit is equivalent to: +// !is_convertible_v(FWD(p))), first_type> || +// !is_convertible_v(FWD(p))), second_type>. +// clang-format off +static_assert( std::is_convertible_v&&, + std::pair, ConvertibleFrom>>); +static_assert(!std::is_convertible_v&&, + std::pair, ExplicitConstructibleFrom>>); +static_assert(!std::is_convertible_v&&, + std::pair, ConvertibleFrom>>); +static_assert(!std::is_convertible_v&&, + std::pair, ExplicitConstructibleFrom>>); +// clang-format on + +constexpr bool test() { + // simple case: init pair from const pair&& + { + const std::pair p1{1, 2}; + std::pair p2{std::move(p1)}; + assert(&(p2.first) == &(p1.first)); + assert(&(p2.second) == &(p1.second)); + } + + // test implicit conversions. + { + const std::pair p1{1, 2}; + std::pair, ConvertibleFrom> p2 = std::move(p1); + assert(p2.first.v.val == 1); + assert(p2.second.v == 2); + } + + // test explicit conversions. + { + const std::pair p1{1, 2}; + std::pair, ExplicitConstructibleFrom> p2{std::move(p1)}; + assert(p2.first.v.val == 1); + assert(p2.second.v == 2); + } + + // test correct constructors of underlying types are called + { + const std::pair p1{}; + std::pair, ConvertibleFrom> p2{std::move(p1)}; + assert(constMoveCtrCalled(p2.first.v)); + assert(constMoveCtrCalled(p2.second.v)); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor_pair_U_V_ref.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor_pair_U_V_ref.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor_pair_U_V_ref.pass.cpp @@ -0,0 +1,85 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// template struct pair +// template +// constexpr explicit(see below) pair(pair& p); + +#include +#include + +#include "copy_move_types.h" +#include "test_macros.h" + +// Constraints: +// is_constructible_v(FWD(p)))> is true and +// is_constructible_v(FWD(p)))> is true. +struct X {}; +struct Y {}; +struct NotConvertibleToXorY {}; + +// clang-format off +static_assert( std::is_constructible_v, std::pair&>); +static_assert(!std::is_constructible_v, std::pair&>); +static_assert(!std::is_constructible_v, std::pair&>); +static_assert(!std::is_constructible_v, std::pair&>); + +// The expression inside explicit is equivalent to: +// !is_convertible_v(FWD(p))), first_type> || +// !is_convertible_v(FWD(p))), second_type>. +static_assert( std::is_convertible_v&, std::pair, ConvertibleFrom>>); +static_assert(!std::is_convertible_v&, std::pair, ExplicitConstructibleFrom>>); +static_assert(!std::is_convertible_v&, std::pair, ConvertibleFrom>>); +static_assert(!std::is_convertible_v&, std::pair, ExplicitConstructibleFrom>>); +// clang-format on + +constexpr bool test() { + // use case in zip. Init pair from pair& + { + std::pair p1{1, 2}; + std::pair p2{p1}; + assert(&(p2.first) == &(p1.first)); + assert(&(p2.second) == &(p1.second)); + } + + // test implicit conversions. + { + std::pair p1{1, 2}; + std::pair, ConvertibleFrom> p2 = p1; + assert(p2.first.v.val == 1); + assert(p2.second.v == 2); + } + + // test explicit conversions. + { + std::pair p1{1, 2}; + std::pair, ExplicitConstructibleFrom> p2{p1}; + assert(p2.first.v.val == 1); + assert(p2.second.v == 2); + } + + // test correct constructors of underlying types are called + { + std::pair p1{}; + std::pair, ConvertibleFrom> p2{p1}; + assert(nonConstCopyCtrCalled(p2.first.v)); + assert(nonConstCopyCtrCalled(p2.second.v)); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/swap_member_const.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/swap_member_const.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/swap_member_const.pass.cpp @@ -0,0 +1,81 @@ +//===----------------------------------------------------------------------===// +// +// 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 struct pair +// void swap(const pair& p) const; + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +#include +#include + +#include "test_macros.h" + +// Remarks: The expression inside noexcept is equivalent to +// is_nothrow_swappable_v && is_nothrow_swappable_v for the second overload. +template +concept ConstMemberSwapNoexcept = + requires(const T& t1, const T& t2) { + { t1.swap(t2) } noexcept; + }; + +template +struct SwapMayThrow {}; + +template +void swap(const SwapMayThrow&, const SwapMayThrow&) noexcept(!canThrow); + +static_assert(ConstMemberSwapNoexcept, SwapMayThrow>>); +static_assert(!ConstMemberSwapNoexcept, SwapMayThrow>>); +static_assert(!ConstMemberSwapNoexcept, SwapMayThrow>>); +static_assert(!ConstMemberSwapNoexcept, SwapMayThrow>>); + +struct ConstSwappable { + mutable int i; + friend constexpr void swap(const ConstSwappable& lhs, const ConstSwappable& rhs) { std::swap(lhs.i, rhs.i); } +}; + +constexpr bool test() { + // user defined const swap + { + using P = std::pair; + const P p1(ConstSwappable{0}, ConstSwappable{1}); + const P p2(ConstSwappable{2}, ConstSwappable{3}); + p1.swap(p2); + assert(p1.first.i == 2); + assert(p1.second.i == 3); + assert(p2.first.i == 0); + assert(p2.second.i == 1); + } + + // pair of references + { + int i1 = 0, i2 = 1, i3 = 2, i4 = 3; + const std::pair p1{i1, i2}; + const std::pair p2{i3, i4}; + p1.swap(p2); + assert(p1.first == 2); + assert(p1.second == 3); + assert(p2.first == 0); + assert(p2.second == 1); + } + return true; +} + +int main(int, char**) { + test(); + +// gcc cannot have mutable member in constant expression +#if !defined(TEST_COMPILER_GCC) + static_assert(test()); +#endif + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.spec/non_member_const_swap.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.spec/non_member_const_swap.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/utility/pairs/pairs.spec/non_member_const_swap.pass.cpp @@ -0,0 +1,93 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// void swap(const pair& x, const pair& y) const noexcept(noexcept(x.swap(y)));; + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +#include +#include + +#include "test_macros.h" + +// Constraints: +// For the second overload, is_swappable_v is true and is_swappable_v is true. +struct NonConstSwappable { + friend constexpr void swap(NonConstSwappable&, NonConstSwappable&); +}; + +struct ConstSwappable { + mutable int i; + friend constexpr void swap(const ConstSwappable& lhs, const ConstSwappable& rhs) { std::swap(lhs.i, rhs.i); } +}; + +static_assert(std::is_swappable_v>); +static_assert(!std::is_swappable_v>); +static_assert(!std::is_swappable_v>); +static_assert(!std::is_swappable_v>); + +// noexcept(noexcept(x.swap(y))); +template +concept NonMemberSwapNoexcept = + requires(T t1, T t2) { + { swap(t1, t2) } noexcept; + }; + +template +struct SwapMayThrow {}; + +template +void swap(const SwapMayThrow&, const SwapMayThrow&) noexcept(!canThrow); + +static_assert(NonMemberSwapNoexcept, SwapMayThrow>>); +static_assert(!NonMemberSwapNoexcept, SwapMayThrow>>); +static_assert(!NonMemberSwapNoexcept, SwapMayThrow>>); +static_assert(!NonMemberSwapNoexcept, SwapMayThrow>>); + +constexpr bool test() { + // user defined const swap + { + using P = std::pair; + const P p1(ConstSwappable{0}, ConstSwappable{1}); + const P p2(ConstSwappable{2}, ConstSwappable{3}); + using std::swap; + swap(p1, p2); + assert(p1.first.i == 2); + assert(p1.second.i == 3); + assert(p2.first.i == 0); + assert(p2.second.i == 1); + } + + // pair of references + { + int i1 = 0, i2 = 1, i3 = 2, i4 = 3; + const std::pair p1{i1, i2}; + const std::pair p2{i3, i4}; + using std::swap; + swap(p1, p2); + assert(p1.first == 2); + assert(p1.second == 3); + assert(p2.first == 0); + assert(p2.second == 1); + } + return true; +} + +int main(int, char**) { + test(); + +// gcc cannot have mutable member in constant expression +#if !defined(TEST_COMPILER_GCC) + static_assert(test()); +#endif + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_types.h b/libcxx/test/support/copy_move_types.h rename from libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_types.h rename to libcxx/test/support/copy_move_types.h --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_types.h +++ b/libcxx/test/support/copy_move_types.h @@ -10,6 +10,9 @@ #include "test_allocator.h" #include +#include + +// Types that can be used to test copy/move operations struct MutableCopy { int val; @@ -215,4 +218,127 @@ template concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test({std::forward(args)...}); }; +struct CopyAssign { + int val; + + constexpr CopyAssign() = default; + constexpr CopyAssign(int v) : val(v) {} + + constexpr CopyAssign& operator=(const CopyAssign&) = default; + + constexpr const CopyAssign& operator=(const CopyAssign&) const = delete; + constexpr CopyAssign& operator=(CopyAssign&&) = delete; + constexpr const CopyAssign& operator=(CopyAssign&&) const = delete; +}; + +struct ConstCopyAssign { + mutable int val; + + constexpr ConstCopyAssign() = default; + constexpr ConstCopyAssign(int v) : val(v) {} + + constexpr const ConstCopyAssign& operator=(const ConstCopyAssign& other) const { + val = other.val; + return *this; + } + + constexpr ConstCopyAssign& operator=(const ConstCopyAssign&) = delete; + constexpr ConstCopyAssign& operator=(ConstCopyAssign&&) = delete; + constexpr const ConstCopyAssign& operator=(ConstCopyAssign&&) const = delete; +}; + +struct MoveAssign { + int val; + + constexpr MoveAssign() = default; + constexpr MoveAssign(int v) : val(v) {} + + constexpr MoveAssign& operator=(MoveAssign&&) = default; + + constexpr MoveAssign& operator=(const MoveAssign&) = delete; + constexpr const MoveAssign& operator=(const MoveAssign&) const = delete; + constexpr const MoveAssign& operator=(MoveAssign&&) const = delete; +}; + +struct ConstMoveAssign { + mutable int val; + + constexpr ConstMoveAssign() = default; + constexpr ConstMoveAssign(int v) : val(v) {} + + constexpr const ConstMoveAssign& operator=(ConstMoveAssign&& other) const { + val = other.val; + return *this; + } + + constexpr ConstMoveAssign& operator=(const ConstMoveAssign&) = delete; + constexpr const ConstMoveAssign& operator=(const ConstMoveAssign&) const = delete; + constexpr ConstMoveAssign& operator=(ConstMoveAssign&&) = delete; +}; + +template +struct AssignableFrom { + T v; + + constexpr AssignableFrom() = default; + + template + constexpr AssignableFrom(U&& u) + requires std::is_constructible_v + : v(std::forward(u)) {} + + constexpr AssignableFrom& operator=(const T& t) + requires std::is_copy_assignable_v + { + v = t; + return *this; + } + + constexpr AssignableFrom& operator=(T&& t) + requires std::is_move_assignable_v + { + v = std::move(t); + return *this; + } + + constexpr const AssignableFrom& operator=(const T& t) const + requires std::is_assignable_v + { + v = t; + return *this; + } + + constexpr const AssignableFrom& operator=(T&& t) const + requires std::is_assignable_v + { + v = std::move(t); + return *this; + } +}; + +struct TracedAssignment { + int copyAssign = 0; + mutable int constCopyAssign = 0; + int moveAssign = 0; + mutable int constMoveAssign = 0; + + constexpr TracedAssignment() = default; + + constexpr TracedAssignment& operator=(const TracedAssignment&) { + copyAssign++; + return *this; + } + constexpr const TracedAssignment& operator=(const TracedAssignment&) const { + constCopyAssign++; + return *this; + } + constexpr TracedAssignment& operator=(TracedAssignment&&) { + moveAssign++; + return *this; + } + constexpr const TracedAssignment& operator=(TracedAssignment&&) const { + constMoveAssign++; + return *this; + } +}; #endif