Fix self-move handling in absl::linked_hash_{set|map}
PiperOrigin-RevId: 853340964
Change-Id: I818695f01d53b5515be24122e1988e69ad6421d4
diff --git a/absl/container/linked_hash_map.h b/absl/container/linked_hash_map.h
index 8348011..e42a1f7 100644
--- a/absl/container/linked_hash_map.h
+++ b/absl/container/linked_hash_map.h
@@ -248,22 +248,25 @@
}
linked_hash_map& operator=(const linked_hash_map& other) {
- if (this == &other) return *this;
- // Make a new set, with other's hash/eq/alloc.
- set_ = SetType(other.bucket_count(), other.set_.hash_function(),
- other.set_.key_eq(), other.get_allocator());
- // Copy the list, with other's allocator.
- list_ = ListType(other.get_allocator());
- CopyFrom(other);
+ if (this != &other) {
+ // Make a new set, with other's hash/eq/alloc.
+ set_ = SetType(other.bucket_count(), other.set_.hash_function(),
+ other.set_.key_eq(), other.get_allocator());
+ // Copy the list, with other's allocator.
+ list_ = ListType(other.get_allocator());
+ CopyFrom(other);
+ }
return *this;
}
linked_hash_map& operator=(linked_hash_map&& other) noexcept {
- // underlying containers will handle progagate_on_container_move details
- set_ = std::move(other.set_);
- list_ = std::move(other.list_);
- other.set_.clear();
- other.list_.clear();
+ if (this != &other) {
+ // underlying containers will handle progagate_on_container_move details
+ set_ = std::move(other.set_);
+ list_ = std::move(other.list_);
+ other.set_.clear();
+ other.list_.clear();
+ }
return *this;
}
diff --git a/absl/container/linked_hash_map_test.cc b/absl/container/linked_hash_map_test.cc
index 54d42fe..9f530d7 100644
--- a/absl/container/linked_hash_map_test.cc
+++ b/absl/container/linked_hash_map_test.cc
@@ -98,6 +98,14 @@
FAIL() << "Assigned map's find method returned an invalid iterator.";
}
+// Tests that self-assignment works.
+TEST(LinkedHashMapTest, SelfAssign) {
+ linked_hash_map<int, int> a{{1, 1}, {2, 2}, {3, 3}};
+ auto& a_ref = a;
+ a = a_ref;
+ EXPECT_THAT(a, ElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3)));
+}
+
// Tests that move constructor works.
TEST(LinkedHashMapTest, Move) {
// Use unique_ptr as an example of a non-copyable type.
@@ -108,6 +116,14 @@
EXPECT_THAT(n, ElementsAre(Pair(2, Pointee(12)), Pair(3, Pointee(13))));
}
+// Tests that self-moving works.
+TEST(LinkedHashMapTest, SelfMove) {
+ linked_hash_map<int, int> a{{1, 1}, {2, 2}, {3, 3}};
+ auto& a_ref = a;
+ a = std::move(a_ref);
+ EXPECT_THAT(a, ElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3)));
+}
+
TEST(LinkedHashMapTest, CanInsertMoveOnly) {
linked_hash_map<int, std::unique_ptr<int>> m;
struct Data {
@@ -512,6 +528,13 @@
ASSERT_EQ(2, m2.size());
}
+TEST(LinkedHashMapTest, SelfSwap) {
+ linked_hash_map<int, int> a{{1, 1}, {2, 2}, {3, 3}};
+ using std::swap;
+ swap(a, a);
+ EXPECT_THAT(a, ElementsAre(Pair(1, 1), Pair(2, 2), Pair(3, 3)));
+}
+
TEST(LinkedHashMapTest, InitializerList) {
linked_hash_map<int, int> m{{1, 2}, {3, 4}};
ASSERT_EQ(2, m.size());
diff --git a/absl/container/linked_hash_set.h b/absl/container/linked_hash_set.h
index f874cd1..1b5d578 100644
--- a/absl/container/linked_hash_set.h
+++ b/absl/container/linked_hash_set.h
@@ -238,21 +238,24 @@
}
linked_hash_set& operator=(const linked_hash_set& other) {
- if (this == &other) return *this;
- // Make a new set, with other's hash/eq/alloc.
- set_ = SetType(other.bucket_count(), other.set_.hash_function(),
- other.set_.key_eq(), other.get_allocator());
- // Copy the list, with other's allocator.
- list_ = ListType(other.get_allocator());
- CopyFrom(other);
+ if (this != &other) {
+ // Make a new set, with other's hash/eq/alloc.
+ set_ = SetType(other.bucket_count(), other.set_.hash_function(),
+ other.set_.key_eq(), other.get_allocator());
+ // Copy the list, with other's allocator.
+ list_ = ListType(other.get_allocator());
+ CopyFrom(other);
+ }
return *this;
}
linked_hash_set& operator=(linked_hash_set&& other) noexcept {
- set_ = std::move(other.set_);
- list_ = std::move(other.list_);
- other.set_.clear();
- other.list_.clear();
+ if (this != &other) {
+ set_ = std::move(other.set_);
+ list_ = std::move(other.list_);
+ other.set_.clear();
+ other.list_.clear();
+ }
return *this;
}
diff --git a/absl/container/linked_hash_set_test.cc b/absl/container/linked_hash_set_test.cc
index b641b97..9a3af62 100644
--- a/absl/container/linked_hash_set_test.cc
+++ b/absl/container/linked_hash_set_test.cc
@@ -91,6 +91,21 @@
FAIL() << "Assigned set's find method returned an invalid iterator.";
}
+// Tests that self-assignment works.
+TEST(LinkedHashSetTest, SelfAssign) {
+ linked_hash_set<int> a{1, 2, 3};
+ auto& a_ref = a;
+ a = a_ref;
+
+ EXPECT_TRUE(a.contains(2));
+ auto found = a.find(2);
+ ASSERT_TRUE(found != a.end());
+ for (auto iter = a.begin(); iter != a.end(); ++iter) {
+ if (iter == found) return;
+ }
+ FAIL() << "Assigned set's find method returned an invalid iterator.";
+}
+
// Tests that move constructor works.
TEST(LinkedHashSetTest, Move) {
// Use unique_ptr as an example of a non-copyable type.
@@ -101,6 +116,14 @@
EXPECT_THAT(n, ElementsAre(Pointee(2), Pointee(3)));
}
+// Tests that self-moving works.
+TEST(LinkedHashSetTest, SelfMove) {
+ linked_hash_set<int> a{1, 2, 3};
+ auto& a_ref = a;
+ a = std::move(a_ref);
+ EXPECT_THAT(a, ElementsAre(1, 2, 3));
+}
+
struct IntUniquePtrHash {
size_t operator()(const std::unique_ptr<int>& p) const {
return static_cast<size_t>(*p);
@@ -521,6 +544,13 @@
ASSERT_EQ(2, m2.size());
}
+TEST(LinkedHashSetTest, SelfSwap) {
+ linked_hash_set<int> a{1, 2, 3};
+ using std::swap;
+ swap(a, a);
+ EXPECT_THAT(a, ElementsAre(1, 2, 3));
+}
+
TEST(LinkedHashSetTest, InitializerList) {
linked_hash_set<int> m{1, 3};
ASSERT_EQ(2, m.size());