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