Skip to content

Commit f6aab98

Browse files
authored
[Bugfix][Support] Fix copy constructor for support::OrderedSet (#17044)
Prior to this commit, the `support::OrderedSet<T>` utility used the default copy constructor and copy assignment, which would copy both the `OrderedSet::elements_` and `OrderedSet::elem_to_iter_` members. While this is the correct behavior for `elements_`, the copy of `elem_to_iter_` would contain references to the original's `element_`, rather than to its own. While `elem_to_iter_` is used in both `OrderedSet::push_back` and `OrderedSet::erase`, the implementation of `OrderedSet::push_back` only depends on the keys used in `elem_to_iter_`, and does not depend on the values stored. As a result, this bug could go undetected for append-only usage, which is the most frequent use of `OrderedSet`. This commit updates `support::OrderedSet` to have an explicit copy constructor and copy assignment. Only the `std::list<T> elements_` member may be copied, while the `elem_to_iter_` must instead be rebuilt.
1 parent 08b32a7 commit f6aab98

File tree

1 file changed

+27
-4
lines changed

1 file changed

+27
-4
lines changed

src/support/ordered_set.h

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,28 @@ class OrderedSet {
5454
public:
5555
OrderedSet() = default;
5656

57+
/* \brief Explicit copy constructor
58+
*
59+
* The default copy constructor would copy both `elements_` and
60+
* `elem_to_iter_`. While this is the correct behavior for
61+
* `elements_`, the copy of `elem_to_iter_` would contain references
62+
* to the original's `element_`, rather than to its own
63+
*/
64+
OrderedSet(const OrderedSet<T>& other) : elements_(other.elements_) { InitElementToIter(); }
65+
66+
/* \brief Explicit copy assignment
67+
*
68+
* Implemented in terms of the copy constructor, and the default
69+
* move assignment.
70+
*/
71+
OrderedSet& operator=(const OrderedSet<T>& other) { return *this = OrderedSet(other); }
72+
73+
OrderedSet(OrderedSet<T>&&) = default;
74+
OrderedSet& operator=(OrderedSet<T>&&) = default;
75+
5776
template <typename Iter>
58-
OrderedSet(Iter begin, Iter end) {
59-
for (auto it = begin; it != end; it++) {
60-
push_back(*it);
61-
}
77+
OrderedSet(Iter begin, Iter end) : elements_(begin, end) {
78+
InitElementToIter();
6279
}
6380

6481
void push_back(const T& t) {
@@ -90,6 +107,12 @@ class OrderedSet {
90107
auto empty() const { return elements_.empty(); }
91108

92109
private:
110+
void InitElementToIter() {
111+
for (auto it = elements_.begin(); it != elements_.end(); it++) {
112+
elem_to_iter_[*it] = it;
113+
}
114+
}
115+
93116
std::list<T> elements_;
94117
typename detail::OrderedSetLookupType<T>::MapType elem_to_iter_;
95118
};

0 commit comments

Comments
 (0)