diff --git a/libcxx/docs/Status/Cxx2bPapers.csv b/libcxx/docs/Status/Cxx2bPapers.csv --- a/libcxx/docs/Status/Cxx2bPapers.csv +++ b/libcxx/docs/Status/Cxx2bPapers.csv @@ -36,5 +36,5 @@ "`P2321 `__","LWG","``zip``","October 2021","","" "`P2340 `__","LWG","Clarifying the status of the 'C headers'","October 2021","","" "`P2393 `__","LWG","Cleaning up ``integer``-class types","October 2021","","" -"`P2401 `__","LWG","Add a conditional ``noexcept`` specification to ``std::exchange``","October 2021","","" +"`P2401 `__","LWG","Add a conditional ``noexcept`` specification to ``std::exchange``","October 2021","|Complete|","14.0" "","","","","","" diff --git a/libcxx/include/__utility/exchange.h b/libcxx/include/__utility/exchange.h --- a/libcxx/include/__utility/exchange.h +++ b/libcxx/include/__utility/exchange.h @@ -12,6 +12,7 @@ #include <__config> #include <__utility/forward.h> #include <__utility/move.h> +#include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) #pragma GCC system_header @@ -22,7 +23,8 @@ #if _LIBCPP_STD_VER > 11 template inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 -_T1 exchange(_T1& __obj, _T2 && __new_value) +_T1 exchange(_T1& __obj, _T2&& __new_value) + noexcept(is_nothrow_move_constructible<_T1>::value && is_nothrow_assignable<_T1&, _T2>::value) { _T1 __old_value = _VSTD::move(__obj); __obj = _VSTD::forward<_T2>(__new_value); diff --git a/libcxx/include/utility b/libcxx/include/utility --- a/libcxx/include/utility +++ b/libcxx/include/utility @@ -183,7 +183,8 @@ using index_sequence_for = make_index_sequence; template - T exchange(T& obj, U&& new_value); + constexpr T exchange(T& obj, U&& new_value) + noexcept(is_nothrow_move_constructible::value && is_nothrow_assignable::value); // constexpr in C++17, noexcept in C++23 // 20.2.7, in-place construction // C++17 struct in_place_t { diff --git a/libcxx/test/std/utilities/utility/exchange/exchange.pass.cpp b/libcxx/test/std/utilities/utility/exchange/exchange.pass.cpp --- a/libcxx/test/std/utilities/utility/exchange/exchange.pass.cpp +++ b/libcxx/test/std/utilities/utility/exchange/exchange.pass.cpp @@ -13,7 +13,8 @@ // template // constexpr T // constexpr after C++17 -// exchange(T& obj, U&& new_value); +// exchange(T& obj, U&& new_value) +// noexcept(is_nothrow_move_constructible::value && is_nothrow_assignable::value); #include #include @@ -37,7 +38,36 @@ } #endif - +template +struct TestNoexcept { + TestNoexcept() = default; + TestNoexcept(const TestNoexcept&); + TestNoexcept(TestNoexcept&&) noexcept(Move); + TestNoexcept& operator=(const TestNoexcept&); + TestNoexcept& operator=(TestNoexcept&&) noexcept(Assign); +}; + +constexpr bool test_noexcept() { + { + int x = 42; + ASSERT_NOEXCEPT(std::exchange(x, 42)); + } + { + TestNoexcept x; + ASSERT_NOEXCEPT(std::exchange(x, std::move(x))); + ASSERT_NOT_NOEXCEPT(std::exchange(x, x)); // copy-assignment is not noexcept + } + { + TestNoexcept x; + ASSERT_NOT_NOEXCEPT(std::exchange(x, std::move(x))); + } + { + TestNoexcept x; + ASSERT_NOT_NOEXCEPT(std::exchange(x, std::move(x))); + } + + return true; +} int main(int, char**) { @@ -81,5 +111,7 @@ static_assert(test_constexpr()); #endif + static_assert(test_noexcept(), ""); + return 0; }