Add limited support for rvalue ranges in absl::c_copy*() and absl::c_move() algorithms PiperOrigin-RevId: 931236330 Change-Id: I5dcf1356b36c604c31bb7e1deff307925c86ad06
diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h index 6928623..fef2911 100644 --- a/absl/algorithm/container.h +++ b/absl/algorithm/container.h
@@ -192,16 +192,6 @@ std::remove_reference_t<C>>::value, std::decay_t<OutputIterator>>; -template <typename C, typename OutputRange> -using ResultOfRangeToRangeTransfer = - std::enable_if_t<container_algorithm_internal::HasBeginEnd< - std::add_lvalue_reference_t<OutputRange>>::value && - !container_algorithm_internal::IsMultidimensionalArray< - std::remove_reference_t<OutputRange>>::value && - !container_algorithm_internal::IsMultidimensionalArray< - std::remove_reference_t<C>>::value, - void>; - // Similar to std::is_pointer, but for testing if a type is a span. // // Note that subclasses of spans do not automatically qualify as spans, as they @@ -231,6 +221,18 @@ std::conditional_t<std::is_lvalue_reference<C>::value, std::true_type, IsSpan<C>>; +template <typename C, typename OutputRange> +using ResultOfRangeToRangeTransfer = + std::enable_if_t<container_algorithm_internal::HasBeginEnd< + std::add_lvalue_reference_t<OutputRange>>::value && + !container_algorithm_internal::IsMultidimensionalArray< + std::remove_reference_t<OutputRange>>::value && + !container_algorithm_internal::IsMultidimensionalArray< + std::remove_reference_t<C>>::value && + container_algorithm_internal:: + IsPermissibleDestinationRange<OutputRange>::value, + void>; + } // namespace container_algorithm_internal // PUBLIC API @@ -632,8 +634,7 @@ InputSequence, OutputRange> c_copy(const InputSequence& input, OutputRange&& output) { container_algorithm_internal::AssertCopySize(input, output); - absl::c_copy(input, container_algorithm_internal::c_begin( - std::forward<OutputRange>(output))); + absl::c_copy(input, container_algorithm_internal::c_begin(output)); } // c_copy_n() @@ -664,9 +665,7 @@ C, OutputRange> c_copy_n(const C& input, Size n, OutputRange&& output) { container_algorithm_internal::AssertCopyNSize(input, n, output); - absl::c_copy_n( - input, n, - container_algorithm_internal::c_begin(std::forward<OutputRange>(output))); + absl::c_copy_n(input, n, container_algorithm_internal::c_begin(output)); } // c_copy_if() @@ -715,8 +714,8 @@ C, OutputRange> c_move(C&& src, OutputRange&& dest) { container_algorithm_internal::AssertCopySize(src, dest); - absl::c_move(std::forward<C>(src), container_algorithm_internal::c_begin( - std::forward<OutputRange>(dest))); + absl::c_move(std::forward<C>(src), + container_algorithm_internal::c_begin(dest)); } // c_move_backward()
diff --git a/absl/algorithm/container_test.cc b/absl/algorithm/container_test.cc index 6249e20..0b67001 100644 --- a/absl/algorithm/container_test.cc +++ b/absl/algorithm/container_test.cc
@@ -762,6 +762,13 @@ EXPECT_THAT(actual, ElementsAre(1, 2, 3, 4, 5)); } +TEST(MutatingTest, CopyToSpanRvalue) { + const std::vector<int> input = {1, 2, 3}; + std::vector<int> actual = {0, 0, 0, 4, 5}; + absl::c_copy(input, absl::MakeSpan(actual)); + EXPECT_THAT(actual, ElementsAre(1, 2, 3, 4, 5)); +} + TEST(MutatingTest, CopyNToContainer) { const std::vector<int> input = {1, 2, 3, 4, 5}; std::vector<int> actual = {0, 0, 0, 0, 0}; @@ -769,6 +776,13 @@ EXPECT_THAT(actual, ElementsAre(1, 2, 0, 0, 0)); } +TEST(MutatingTest, CopyNToSpanRvalue) { + const std::vector<int> input = {1, 2, 3, 4, 5}; + std::vector<int> actual = {0, 0, 0, 0, 0}; + absl::c_copy_n(input, 2, absl::MakeSpan(actual)); + EXPECT_THAT(actual, ElementsAre(1, 2, 0, 0, 0)); +} + TEST(MutatingTest, CopyNToContainerWithZeroN) { const std::vector<int> input = {1, 2, 3, 4, 5}; std::vector<int> actual = {0, 0, 0, 0, 0}; @@ -1008,6 +1022,28 @@ EXPECT_EQ(actual[3], nullptr); } +TEST(MutatingTest, MoveToSpanRvalue) { + std::vector<std::unique_ptr<int>> input; + input.push_back(std::make_unique<int>(1)); + input.push_back(std::make_unique<int>(2)); + input.push_back(std::make_unique<int>(3)); + + std::vector<std::unique_ptr<int>> actual(5); + absl::c_move(input, absl::MakeSpan(actual)); + + EXPECT_EQ(input[0], nullptr); + EXPECT_EQ(input[1], nullptr); + EXPECT_EQ(input[2], nullptr); + + ASSERT_NE(actual[0], nullptr); + EXPECT_EQ(*actual[0], 1); + ASSERT_NE(actual[1], nullptr); + EXPECT_EQ(*actual[1], 2); + ASSERT_NE(actual[2], nullptr); + EXPECT_EQ(*actual[2], 3); + EXPECT_EQ(actual[3], nullptr); +} + TEST(MutatingTest, MoveToDifferentContainerType) { std::list<std::unique_ptr<int>> input; input.push_back(std::make_unique<int>(1)); @@ -2624,12 +2660,25 @@ using Vec = std::vector<int>; // Because AmbiguousType is both an iterator and a container, // the compiler should fail to resolve the c_copy overload. - static_assert(!CanCopy<Vec, AmbiguousType>::value, + static_assert(!CanCopy<Vec, AmbiguousType&>::value, "Ambiguous types should not compile!"); - static_assert(!CanCopyN<Vec, AmbiguousType>::value, + static_assert(!CanCopyN<Vec, AmbiguousType&>::value, "Ambiguous types should not compile!"); } +TEST(CanCopyTest, CopyToRValue) { + using Vec = std::vector<int>; + using Span = absl::Span<int>; + static_assert(CanCopy<Vec, Span>::value, + "Should be able to copy to rvalue Span"); + static_assert(!CanCopy<Vec, Vec>::value, + "Should not be able to copy to rvalue vector"); + static_assert(CanCopyN<Vec, Span>::value, + "Should be able to copy_n to rvalue Span"); + static_assert(!CanCopyN<Vec, Vec>::value, + "Should not be able to copy_n to rvalue vector"); +} + TEST(CanMoveTest, MoveToMultiDimArray) { static_assert(CanMove<std::vector<int>, int (&)[10]>::value); static_assert(!CanMove<std::vector<int>, int (&)[2][2]>::value); @@ -2644,10 +2693,19 @@ using Vec = std::vector<int>; // Because AmbiguousType is both an iterator and a container, // the compiler should fail to resolve the c_move overload. - static_assert(!CanMove<Vec, AmbiguousType>::value, + static_assert(!CanMove<Vec, AmbiguousType&>::value, "Ambiguous types should not compile!"); } +TEST(CanMoveTest, MoveToRValue) { + using Vec = std::vector<std::unique_ptr<int>>; + using Span = absl::Span<std::unique_ptr<int>>; + static_assert(CanMove<Vec, Span>::value, + "Should be able to move to rvalue Span"); + static_assert(!CanMove<Vec, Vec>::value, + "Should not be able to move to rvalue vector"); +} + template <typename C, typename T, typename = void> struct CanFill : std::false_type {};