Add an overload of absl::c_move to move between containers.

This change introduces a new overload for absl::c_move that takes two ranges, allowing elements to be moved from a source container to a destination container. The destination container must be fixed size so we can perform bounds checking.

PiperOrigin-RevId: 919189227
Change-Id: Ica35c5d8bd59ebe16564f2b2f490770899ad16f8
diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h
index 965c545..c0934f7 100644
--- a/absl/algorithm/container.h
+++ b/absl/algorithm/container.h
@@ -686,10 +686,36 @@
 // Container-based version of the <algorithm> `std::move()` function to move
 // a container's elements into an iterator.
 template <typename C, typename OutputIterator>
-ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20 OutputIterator c_move(C&& src,
-                                                          OutputIterator dest) {
+ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20
+    std::enable_if_t<container_algorithm_internal::IsIterator<
+                         absl::remove_cvref_t<OutputIterator>>::value &&
+                         !container_algorithm_internal::IsMultidimensionalArray<
+                             std::remove_reference_t<C>>::value,
+                     std::decay_t<OutputIterator>>
+    c_move(C&& src, OutputIterator&& dest) {
   return std::move(container_algorithm_internal::c_begin(src),
-                   container_algorithm_internal::c_end(src), dest);
+                   container_algorithm_internal::c_end(src),
+                   std::forward<OutputIterator>(dest));
+}
+
+// Moves elements from `src` to `dest`. `absl::c_move(src, dest)` is
+// equivalent to `std::move(std::begin(src), std::end(src), std::begin(dest))`.
+//
+// The `dest` container must be large enough to hold all elements of `src`;
+// this function does not resize `dest`.
+template <typename C, typename OutputRange>
+ABSL_INTERNAL_CONSTEXPR_SINCE_CXX20
+    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>
+    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)));
 }
 
 // c_move_backward()
diff --git a/absl/algorithm/container_test.cc b/absl/algorithm/container_test.cc
index fcb8492..6bacb1e 100644
--- a/absl/algorithm/container_test.cc
+++ b/absl/algorithm/container_test.cc
@@ -957,6 +957,95 @@
                                 Pointee(2), Pointee(3)));
 }
 
+TEST(MutatingTest, MoveToContainer) {
+  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, 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));
+  input.push_back(std::make_unique<int>(2));
+
+  std::array<std::unique_ptr<int>, 3> actual;
+  absl::c_move(input, actual);
+
+  EXPECT_EQ(input.front(), nullptr);
+  ASSERT_NE(actual[0], nullptr);
+  EXPECT_EQ(*actual[0], 1);
+}
+
+TEST(MutatingTest, MoveToCArray) {
+  std::vector<std::unique_ptr<int>> input;
+  input.push_back(std::make_unique<int>(1));
+  input.push_back(std::make_unique<int>(2));
+
+  std::unique_ptr<int> actual[3];
+  absl::c_move(input, actual);
+
+  EXPECT_EQ(input.front(), nullptr);
+  ASSERT_NE(actual[0], nullptr);
+  EXPECT_EQ(*actual[0], 1);
+}
+
+TEST(MutatingTest, MoveFromCArray) {
+  std::unique_ptr<int> input[2];
+  input[0] = std::make_unique<int>(1);
+  input[1] = std::make_unique<int>(2);
+
+  std::vector<std::unique_ptr<int>> actual(3);
+  absl::c_move(input, actual);
+
+  EXPECT_EQ(input[0], nullptr);
+  ASSERT_NE(actual[0], nullptr);
+  EXPECT_EQ(*actual[0], 1);
+}
+
+#if GTEST_HAS_DEATH_TEST
+
+TEST(MutatingTest, MoveToCArrayInvalidSize) {
+  std::vector<int> input = {1, 2, 3};
+  int actual[2] = {0, 0};
+  if (IsHardened()) {
+    EXPECT_DEATH(absl::c_move(input, actual), "");
+  }
+}
+
+TEST(MutatingTest, MoveToContainerInvalidSize) {
+  std::list<int> input = {1, 2, 3, 4, 5};
+  std::list<int> actual = {0, 0, 0};
+  if (IsHardened()) {
+    EXPECT_DEATH(absl::c_move(input, actual), "");
+  }
+}
+
+TEST(MutatingTest, MoveToForwardListInvalidSize) {
+  std::forward_list<int> input = {1, 2, 3, 4, 5};
+  std::forward_list<int> actual = {0, 0, 0};
+  if (IsHardened()) {
+    EXPECT_DEATH(absl::c_move(input, actual), "");
+  }
+}
+
+#endif  // GTEST_HAS_DEATH_TEST
+
 TEST(MutatingTest, SwapRanges) {
   std::vector<int> odds = {2, 4, 6};
   std::vector<int> evens = {1, 3, 5};
@@ -2459,6 +2548,14 @@
                     std::declval<Container>(), std::declval<ptrdiff_t>(),
                     std::declval<Output>()))>> : std::true_type {};
 
+template <typename Container, typename Output, typename = void>
+struct CanMove : std::false_type {};
+template <typename Container, typename Output>
+struct CanMove<Container, Output,
+               absl::void_t<decltype(absl::c_move(std::declval<Container>(),
+                                                  std::declval<Output>()))>>
+    : std::true_type {};
+
 TEST(CanCopyTest, CopyToMultiDimArray) {
   static_assert(CanCopy<std::vector<int>, int (&)[10]>::value);
   static_assert(!CanCopy<std::vector<int>, int (&)[2][2]>::value);
@@ -2491,4 +2588,22 @@
   static_assert(!CanCopyN<Vec, AmbiguousType>::value,
                 "Ambiguous types should not compile!");
 }
+
+TEST(CanMoveTest, MoveToMultiDimArray) {
+  static_assert(CanMove<std::vector<int>, int (&)[10]>::value);
+  static_assert(!CanMove<std::vector<int>, int (&)[2][2]>::value);
+
+  static_assert(CanMove<int[10], int (&)[10]>::value);
+  static_assert(!CanMove<int[10], int (&)[2][2]>::value);
+  static_assert(!CanMove<int[2][2], int (&)[4]>::value);
+  static_assert(!CanMove<int[2][2], int (&)[2][2]>::value);
+}
+
+TEST(CanMoveTest, AmbiguousTypeFailsToCompile) {
+  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,
+                "Ambiguous types should not compile!");
+}
 }  // namespace