From 4bf6ed6ce8d79857658d763e368ee56617542b2c Mon Sep 17 00:00:00 2001 From: vcoselev <145198867+vcoselev@users.noreply.github.com> Date: Sat, 11 Jul 2026 12:05:39 +0100 Subject: [PATCH] Fix for 1405: etl::exchange doesn't work with unique_ptr (#1477) * Separate bit order and endianness in bit_stream.h (#1495) * Factor out ranges tests from test_algorithm.cpp to test_algorithm_ranges.cpp (#1497) * Fix exchange to use forwarding reference (U&&) and move - etl::exchange was using the copy constructor. Changed for etl::move. - The assignment to the new object is now done with forward. - Added a test with unique_ptr. --------- Co-authored-by: Roland Reichwein Co-authored-by: John Wellbelove --- include/etl/utility.h | 26 ++++++++++++++++++++------ test/test_utility.cpp | 27 +++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/include/etl/utility.h b/include/etl/utility.h index 55f35033..addd7ca1 100644 --- a/include/etl/utility.h +++ b/include/etl/utility.h @@ -490,6 +490,7 @@ namespace etl }; #if ETL_NOT_USING_STL || ETL_CPP14_NOT_SUPPORTED + #if ETL_CPP11_NOT_SUPPORTED //*************************************************************************** /// exchange (const) //*************************************************************************** @@ -500,7 +501,6 @@ namespace etl object = new_value; return old_value; } - template T exchange(T& object, const U& new_value) { @@ -508,16 +508,30 @@ namespace etl object = new_value; return old_value; } -#else + #else //*************************************************************************** - /// exchange (const) + /// exchange //*************************************************************************** template - T exchange(T& object, const U& new_value) + ETL_CONSTEXPR14 T exchange(T& object, U&& new_value) + ETL_NOEXCEPT_IF((etl::is_nothrow_move_constructible::value && etl::is_nothrow_assignable::value)) { - return std::exchange(object, new_value); + T old_value = etl::move(object); + object = etl::forward(new_value); + return old_value; } -#endif + #endif // ETL_CPP11_NOT_SUPPORTED +#else + //*************************************************************************** + /// exchange + //*************************************************************************** + template + ETL_CONSTEXPR14 T exchange(T& object, U&& new_value) + ETL_NOEXCEPT_IF((etl::is_nothrow_move_constructible::value && etl::is_nothrow_assignable::value)) + { + return std::exchange(object, etl::forward(new_value)); + } +#endif // ETL_NOT_USING_STL || ETL_CPP14_NOT_SUPPORTED //*************************************************************************** /// as_const diff --git a/test/test_utility.cpp b/test/test_utility.cpp index 5c01d079..5b824aa3 100644 --- a/test/test_utility.cpp +++ b/test/test_utility.cpp @@ -28,6 +28,7 @@ SOFTWARE. #include "unit_test_framework.h" +#include "etl/memory.h" #include "etl/utility.h" #include @@ -315,6 +316,32 @@ namespace CHECK_EQUAL(1, c); } + //************************************************************************* + TEST(test_exchange_unique_ptr) + { + etl::unique_ptr p1(new int(1)); + etl::unique_ptr p2 = etl::exchange(p1, nullptr); + + CHECK_FALSE(p1); + CHECK_TRUE(p2); + CHECK_EQUAL(*p2, 1); + } + + //************************************************************************* + TEST(test_exchange_unique_ptr_move_in) + { + etl::unique_ptr a(new int(10)); + etl::unique_ptr b(new int(20)); + + etl::unique_ptr old = etl::exchange(a, etl::move(b)); + + CHECK_TRUE(old); + CHECK_EQUAL(*old, 10); + CHECK_TRUE(a); + CHECK_EQUAL(*a, 20); + CHECK_FALSE(b); + } + //************************************************************************* TEST(test_as_const) {