aboutsummaryrefslogtreecommitdiff
path: root/libbutl/small-vector.hxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbutl/small-vector.hxx')
-rw-r--r--libbutl/small-vector.hxx34
1 files changed, 31 insertions, 3 deletions
diff --git a/libbutl/small-vector.hxx b/libbutl/small-vector.hxx
index f0594b1..44a3ef5 100644
--- a/libbutl/small-vector.hxx
+++ b/libbutl/small-vector.hxx
@@ -4,8 +4,9 @@
#pragma once
#include <vector>
-#include <cstddef> // size_t
-#include <utility> // move()
+#include <cstddef> // size_t
+#include <utility> // move()
+#include <type_traits> // is_nothrow_move_constructible
#include <libbutl/small-allocator.hxx>
@@ -24,6 +25,9 @@ namespace butl
//
// - swap() is deleted (see notes below).
//
+ // - In contrast to std::vector, the references, pointers, and iterators
+ // referring to elements are invalidated after moving from it.
+ //
template <typename T, std::size_t N>
class small_vector: private small_allocator_buffer<T, N>,
public std::vector<T, small_allocator<T, N>>
@@ -104,7 +108,25 @@ namespace butl
return *this;
}
+ // Note that while the move constructor is implemented via the move
+ // assignment it may not throw if the value type is no-throw move
+ // constructible.
+ //
+ // Specifically, if v.size() > N then allocators evaluate as equal and the
+ // buffer ownership is transferred. Otherwise, the allocators do not
+ // evaluate as equal and the individual elements are move-constructed in
+ // the preallocated buffer.
+ //
+ // Also note that this constructor ends up calling
+ // base_type::operator=(base_type&&) whose noexcept expression evaluates
+ // to false (propagate_on_container_move_assignment and is_always_equal
+ // are false for small_allocator; see std::vector documentation for
+ // details). We, however, assume that the noexcept expression we use here
+ // is strict enough for all "sane" std::vector implementations since
+ // small_allocator never throws directly.
+ //
small_vector (small_vector&& v)
+ noexcept (std::is_nothrow_move_constructible<T>::value)
: base_type (allocator_type (this))
{
if (v.size () <= N)
@@ -118,8 +140,14 @@ namespace butl
v.clear ();
}
+ // Note that when size() <= N and v.size() > N, then allocators of this
+ // and other containers do not evaluate as equal. Thus, the memory for the
+ // new elements is allocated on the heap and so std::bad_alloc can be
+ // thrown. @@ TODO: maybe we could re-implement this case in terms of
+ // swap()?
+ //
small_vector&
- operator= (small_vector&& v)
+ operator= (small_vector&& v) noexcept (false)
{
// VC's implementation of operator=(&&) (both 14 and 15) frees the
// memory and then reallocated with capacity equal to v.size(). This is