FTL: Refine container semantics
Allow constructing StaticVector and SmallVector by moving smaller
convertible vectors, which so far incurred a copy. Allow the same
copy/move conversion for SmallMap.
Consistently with StaticVector, do not require assignable elements for
the SmallVector to be assignable, which notably enables SmallMap to be
assignable despite its const keys.
Allow comparison of convertible containers.
Bug: 185536303
Test: ftl_test
Change-Id: I35923e794ef26178dc3072f514dea7ad5600bc15
diff --git a/include/ftl/static_vector.h b/include/ftl/static_vector.h
index 70f1721..eb83b85 100644
--- a/include/ftl/static_vector.h
+++ b/include/ftl/static_vector.h
@@ -39,6 +39,9 @@
// adheres to standard containers, except the unstable_erase operation that does not preserve order,
// and the replace operation that destructively emplaces.
//
+// Unlike std::vector, T does not require copy/move assignment, so may be an object with const data
+// members, or be const itself.
+//
// StaticVector<T, 1> is analogous to an iterable std::optional.
// StaticVector<T, 0> is an error.
//
@@ -78,7 +81,14 @@
details::ArrayComparators<StaticVector> {
static_assert(N > 0);
+ // For constructor that moves from a smaller convertible vector.
+ template <typename, std::size_t>
+ friend class StaticVector;
+
using details::ArrayTraits<T>::construct_at;
+ using details::ArrayTraits<T>::replace_at;
+ using details::ArrayTraits<T>::in_place_swap_ranges;
+ using details::ArrayTraits<T>::uninitialized_copy;
using Iter = details::ArrayIterators<StaticVector, T>;
friend Iter;
@@ -117,14 +127,18 @@
StaticVector(StaticVector&& other) { swap<true>(other); }
// Copies at most N elements from a smaller convertible vector.
- template <typename U, std::size_t M, typename = std::enable_if_t<M <= N>>
+ template <typename U, std::size_t M>
StaticVector(const StaticVector<U, M>& other)
- : StaticVector(kIteratorRange, other.begin(), other.end()) {}
+ : StaticVector(kIteratorRange, other.begin(), other.end()) {
+ static_assert(N >= M, "Insufficient capacity");
+ }
- // Copies at most N elements from an array.
+ // Copies at most N elements from a smaller convertible array.
template <typename U, std::size_t M>
explicit StaticVector(U (&array)[M])
- : StaticVector(kIteratorRange, std::begin(array), std::end(array)) {}
+ : StaticVector(kIteratorRange, std::begin(array), std::end(array)) {
+ static_assert(N >= M, "Insufficient capacity");
+ }
// Copies at most N elements from the range [first, last).
//
@@ -139,7 +153,18 @@
template <typename Iterator>
StaticVector(IteratorRangeTag, Iterator first, Iterator last)
: size_(std::min(max_size(), static_cast<size_type>(std::distance(first, last)))) {
- std::uninitialized_copy(first, first + size_, begin());
+ uninitialized_copy(first, first + size_, begin());
+ }
+
+ // Moves at most N elements from a smaller convertible vector.
+ template <typename U, std::size_t M>
+ StaticVector(StaticVector<U, M>&& other) {
+ static_assert(N >= M, "Insufficient capacity");
+
+ // Same logic as swap<true>, though M need not be equal to N.
+ std::uninitialized_move(other.begin(), other.end(), begin());
+ std::destroy(other.begin(), other.end());
+ std::swap(size_, other.size_);
}
// Constructs at most N elements. The template arguments T and N are inferred using the
@@ -240,10 +265,7 @@
//
template <typename... Args>
reference replace(const_iterator it, Args&&... args) {
- value_type element{std::forward<Args>(args)...};
- std::destroy_at(it);
- // This is only safe because exceptions are disabled.
- return *construct_at(it, std::move(element));
+ return replace_at(it, std::forward<Args>(args)...);
}
// Appends an element, and returns an iterator to it. If the vector is full, the element is not
@@ -382,7 +404,7 @@
}
// Swap elements [0, min).
- std::swap_ranges(begin(), begin() + min, other.begin());
+ in_place_swap_ranges(begin(), begin() + min, other.begin());
// No elements to move if sizes are equal.
if (min == max) return;