pw_containers: Trivially destructible Vector<T>
- Make pw::Vector<T> trivially destructible when T is.
- Update naming style for non-type template parameters.
Change-Id: Id836d47ac89afa141bdbcd04fd1712dc73684a4f
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/39000
Pigweed-Auto-Submit: Wyatt Hepler <hepler@google.com>
Reviewed-by: Ewout van Bekkum <ewout@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
diff --git a/pw_containers/public/pw_containers/vector.h b/pw_containers/public/pw_containers/vector.h
index f6b7331..3ab452b 100644
--- a/pw_containers/public/pw_containers/vector.h
+++ b/pw_containers/public/pw_containers/vector.h
@@ -32,9 +32,26 @@
using IsIterator = std::negation<
std::is_same<typename std::iterator_traits<I>::value_type, void>>;
-// Used as kMaxSize in the generic-size Vector<T> interface.
+// Used as max_size in the generic-size Vector<T> interface.
PW_INLINE_VARIABLE constexpr size_t kGeneric = size_t(-1);
+// The DestructorHelper is used to make Vector<T> trivially destructible if T
+// is. This could be replaced with a C++20 constraint.
+template <typename VectorClass, bool is_trivially_destructible>
+class DestructorHelper;
+
+template <typename VectorClass>
+class DestructorHelper<VectorClass, true> {
+ public:
+ ~DestructorHelper() = default;
+};
+
+template <typename VectorClass>
+class DestructorHelper<VectorClass, false> {
+ public:
+ ~DestructorHelper() { static_cast<VectorClass*>(this)->clear(); }
+};
+
} // namespace vector_impl
// The Vector class is similar to std::vector, except it is backed by a
@@ -47,7 +64,7 @@
// the maximum size in a variable. This allows Vectors to be used without having
// to know their maximum size at compile time. It also keeps code size small
// since function implementations are shared for all maximum sizes.
-template <typename T, size_t kMaxSize = vector_impl::kGeneric>
+template <typename T, size_t max_size = vector_impl::kGeneric>
class Vector : public Vector<T, vector_impl::kGeneric> {
public:
using typename Vector<T, vector_impl::kGeneric>::value_type;
@@ -63,45 +80,45 @@
using typename Vector<T, vector_impl::kGeneric>::const_reverse_iterator;
// Construct
- Vector() noexcept : Vector<T, vector_impl::kGeneric>(kMaxSize) {}
+ Vector() noexcept : Vector<T, vector_impl::kGeneric>(max_size) {}
Vector(size_type count, const T& value)
- : Vector<T, vector_impl::kGeneric>(kMaxSize, count, value) {}
+ : Vector<T, vector_impl::kGeneric>(max_size, count, value) {}
explicit Vector(size_type count)
- : Vector<T, vector_impl::kGeneric>(kMaxSize, count, T()) {}
+ : Vector<T, vector_impl::kGeneric>(max_size, count, T()) {}
template <
typename Iterator,
typename...,
typename = std::enable_if_t<vector_impl::IsIterator<Iterator>::value>>
Vector(Iterator first, Iterator last)
- : Vector<T, vector_impl::kGeneric>(kMaxSize, first, last) {}
+ : Vector<T, vector_impl::kGeneric>(max_size, first, last) {}
Vector(const Vector& other)
- : Vector<T, vector_impl::kGeneric>(kMaxSize, other) {}
+ : Vector<T, vector_impl::kGeneric>(max_size, other) {}
- template <size_t kOtherMaxSize>
- Vector(const Vector<T, kOtherMaxSize>& other)
- : Vector<T, vector_impl::kGeneric>(kMaxSize, other) {}
+ template <size_t other_max_size>
+ Vector(const Vector<T, other_max_size>& other)
+ : Vector<T, vector_impl::kGeneric>(max_size, other) {}
Vector(Vector&& other) noexcept
- : Vector<T, vector_impl::kGeneric>(kMaxSize, std::move(other)) {}
+ : Vector<T, vector_impl::kGeneric>(max_size, std::move(other)) {}
- template <size_t kOtherMaxSize>
- Vector(Vector<T, kOtherMaxSize>&& other) noexcept
- : Vector<T, vector_impl::kGeneric>(kMaxSize, std::move(other)) {}
+ template <size_t other_max_size>
+ Vector(Vector<T, other_max_size>&& other) noexcept
+ : Vector<T, vector_impl::kGeneric>(max_size, std::move(other)) {}
Vector(std::initializer_list<T> list)
- : Vector<T, vector_impl::kGeneric>(kMaxSize, list) {}
+ : Vector<T, vector_impl::kGeneric>(max_size, list) {}
Vector& operator=(const Vector& other) {
Vector<T>::assign(other.begin(), other.end());
return *this;
}
- template <size_t kOtherMaxSize>
- Vector& operator=(const Vector<T, kOtherMaxSize>& other) noexcept {
+ template <size_t other_max_size>
+ Vector& operator=(const Vector<T, other_max_size>& other) noexcept {
Vector<T>::assign(other.begin(), other.end());
return *this;
}
@@ -111,8 +128,8 @@
return *this;
}
- template <size_t kOtherMaxSize>
- Vector& operator=(Vector<T, kOtherMaxSize>&& other) noexcept {
+ template <size_t other_max_size>
+ Vector& operator=(Vector<T, other_max_size>&& other) noexcept {
Vector<T>::operator=(std::move(other));
return *this;
}
@@ -127,7 +144,7 @@
private:
friend class Vector<T, vector_impl::kGeneric>;
- static_assert(kMaxSize <= std::numeric_limits<size_type>::max());
+ static_assert(max_size <= std::numeric_limits<size_type>::max());
// Provides access to the underlying array as an array of T.
#ifdef __cpp_lib_launder
@@ -148,14 +165,17 @@
// The alignas specifier is required ensure that a zero-length array is
// aligned the same as an array with elements.
alignas(T) std::array<std::aligned_storage_t<sizeof(T), alignof(T)>,
- kMaxSize> array_;
+ max_size> array_;
};
// Defines the generic-sized Vector<T> specialization, which serves as the base
// class for Vector<T> of any maximum size. Except for constructors, all Vector
// methods are implemented on this class.
template <typename T>
-class Vector<T, vector_impl::kGeneric> {
+class Vector<T, vector_impl::kGeneric>
+ : public vector_impl::DestructorHelper<
+ Vector<T, vector_impl::kGeneric>,
+ std::is_trivially_destructible<T>::value> {
public:
using value_type = T;
@@ -178,8 +198,6 @@
// directly. Instead, construct a Vector<T, max_size>. Vectors of any max size
// can be used through a Vector<T> pointer or reference.
- ~Vector() { clear(); }
-
// Assign
Vector& operator=(const Vector& other) {
diff --git a/pw_containers/vector_test.cc b/pw_containers/vector_test.cc
index 24f42ab..9471114 100644
--- a/pw_containers/vector_test.cc
+++ b/pw_containers/vector_test.cc
@@ -15,7 +15,6 @@
#include "pw_containers/vector.h"
#include <cstddef>
-#include <vector>
#include "gtest/gtest.h"
@@ -429,5 +428,21 @@
}
}
+// Test that Vector<T> is trivially destructible when its type is.
+static_assert(std::is_trivially_destructible_v<Vector<int>>);
+static_assert(std::is_trivially_destructible_v<Vector<int, 4>>);
+
+static_assert(std::is_trivially_destructible_v<MoveOnly>);
+static_assert(std::is_trivially_destructible_v<Vector<MoveOnly>>);
+static_assert(std::is_trivially_destructible_v<Vector<MoveOnly, 1>>);
+
+static_assert(std::is_trivially_destructible_v<CopyOnly>);
+static_assert(std::is_trivially_destructible_v<Vector<CopyOnly>>);
+static_assert(std::is_trivially_destructible_v<Vector<CopyOnly, 99>>);
+
+static_assert(!std::is_trivially_destructible_v<Counter>);
+static_assert(!std::is_trivially_destructible_v<Vector<Counter>>);
+static_assert(!std::is_trivially_destructible_v<Vector<Counter, 99>>);
+
} // namespace
} // namespace pw