Commit eb877c83 authored by Chris Sarbora's avatar Chris Sarbora Committed by Facebook GitHub Bot

Let small_vector pop_back a non-assignable type

Summary: Relying on `erase()`, which must be able to move elements when erasing in front of other valid elements, causes compilation failures when trying to erase non-assignable elements at the end of the vector even though no elements actually need to be moved (and thus assigned) in that case. Here we help the compiler out and extract the destruction/resizing logic away from the moving logic.

Reviewed By: yfeldblum, ot, mkatsevVR

Differential Revision: D20718712

fbshipit-source-id: 51c4e6636bc33577494c0c10e95da5f92b58d4b6
parent 93a55057
...@@ -704,8 +704,8 @@ class small_vector : public detail::small_vector_base< ...@@ -704,8 +704,8 @@ class small_vector : public detail::small_vector_base<
} }
void resize(size_type sz) { void resize(size_type sz) {
if (sz < size()) { if (sz <= size()) {
erase(begin() + sz, end()); downsize(sz);
return; return;
} }
makeSize(sz); makeSize(sz);
...@@ -808,7 +808,11 @@ class small_vector : public detail::small_vector_base< ...@@ -808,7 +808,11 @@ class small_vector : public detail::small_vector_base<
} }
void pop_back() { void pop_back() {
erase(end() - 1); // ideally this would be implemented in terms of erase(end() - 1) to reuse
// the higher-level abstraction, but neither Clang or GCC are able to
// optimize it away. if you change this, please verify (with disassembly)
// that the generated code on -O3 (and ideally -O2) stays short
downsize(size() - 1);
} }
iterator insert(const_iterator constp, value_type&& t) { iterator insert(const_iterator constp, value_type&& t) {
...@@ -869,9 +873,12 @@ class small_vector : public detail::small_vector_base< ...@@ -869,9 +873,12 @@ class small_vector : public detail::small_vector_base<
} }
iterator erase(const_iterator q) { iterator erase(const_iterator q) {
// ideally this would be implemented in terms of erase(q, q + 1) to reuse
// the higher-level abstraction, but neither Clang or GCC are able to
// optimize it away. if you change this, please verify (with disassembly)
// that the generated code on -O3 (and ideally -O2) stays short
std::move(unconst(q) + 1, end(), unconst(q)); std::move(unconst(q) + 1, end(), unconst(q));
(data() + size() - 1)->~value_type(); downsize(size() - 1);
this->setSize(size() - 1);
return unconst(q); return unconst(q);
} }
...@@ -880,20 +887,16 @@ class small_vector : public detail::small_vector_base< ...@@ -880,20 +887,16 @@ class small_vector : public detail::small_vector_base<
return unconst(q1); return unconst(q1);
} }
std::move(unconst(q2), end(), unconst(q1)); std::move(unconst(q2), end(), unconst(q1));
for (auto it = (end() - std::distance(q1, q2)); it != end(); ++it) { downsize(size() - std::distance(q1, q2));
it->~value_type();
}
this->setSize(size() - (q2 - q1));
return unconst(q1); return unconst(q1);
} }
void clear() { void clear() {
// Equivalent to erase(begin(), end()), but neither Clang or GCC are able to // ideally this would be implemented in terms of erase(begin(), end()) to
// optimize away the abstraction. // reuse the higher-level abstraction, but neither Clang or GCC are able to
for (auto it = begin(); it != end(); ++it) { // optimize it away. if you change this, please verify (with disassembly)
it->~value_type(); // that the generated code on -O3 (and ideally -O2) stays short
} downsize(0);
this->setSize(0);
} }
template <class Arg> template <class Arg>
...@@ -957,6 +960,14 @@ class small_vector : public detail::small_vector_base< ...@@ -957,6 +960,14 @@ class small_vector : public detail::small_vector_base<
return const_cast<iterator>(it); return const_cast<iterator>(it);
} }
void downsize(size_type sz) {
assert(sz <= size());
for (auto it = (begin() + sz); it != end(); ++it) {
it->~value_type();
}
this->setSize(sz);
}
// The std::false_type argument is part of disambiguating the // The std::false_type argument is part of disambiguating the
// iterator insert functions from integral types (see insert().) // iterator insert functions from integral types (see insert().)
template <class It> template <class It>
......
...@@ -1174,6 +1174,20 @@ TEST(small_vector, SelfCopyAssignmentForVectorOfPair) { ...@@ -1174,6 +1174,20 @@ TEST(small_vector, SelfCopyAssignmentForVectorOfPair) {
EXPECT_EQ(test[0].first, 13); EXPECT_EQ(test[0].first, 13);
} }
namespace {
struct NonAssignableType {
int const i_{};
};
} // namespace
TEST(small_vector, PopBackNonAssignableType) {
small_vector<NonAssignableType> v;
v.emplace_back();
EXPECT_EQ(1, v.size());
v.pop_back();
EXPECT_EQ(0, v.size());
}
TEST(small_vector, erase) { TEST(small_vector, erase) {
small_vector<int> v(3); small_vector<int> v(3);
std::iota(v.begin(), v.end(), 1); std::iota(v.begin(), v.end(), 1);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment