|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
#include <range/v3/iterator.hpp>
|
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(push)
|
|
|
|
#pragma warning(disable : 26495)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
template <class T, std::size_t N = 16>
|
|
|
|
class static_vector
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
using value_type = T;
|
|
|
|
using size_type = std::size_t;
|
|
|
|
using difference_type = std::ptrdiff_t;
|
|
|
|
using reference = value_type&;
|
|
|
|
using const_reference = const value_type&;
|
|
|
|
using pointer = T*;
|
|
|
|
using const_pointer = const T*;
|
|
|
|
using iterator = T*;
|
|
|
|
using const_iterator = const T*;
|
|
|
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
|
|
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
|
|
|
|
|
|
|
static_vector() noexcept : m_size{0} {}
|
|
|
|
|
|
|
|
explicit static_vector(size_type count) : m_size{count}
|
|
|
|
{
|
|
|
|
assert(count <= N);
|
|
|
|
std::uninitialized_value_construct(begin(), end());
|
|
|
|
}
|
|
|
|
|
|
|
|
static_vector(size_type count, const value_type& value) : m_size{count}
|
|
|
|
{
|
|
|
|
assert(count <= N);
|
|
|
|
std::uninitialized_fill(begin(), end(), value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static_vector(const static_vector& other) : m_size{other.m_size}
|
|
|
|
{
|
|
|
|
std::uninitialized_copy(other.begin(), other.end(), begin());
|
|
|
|
}
|
|
|
|
|
|
|
|
static_vector(static_vector&& other) noexcept : m_size{other.m_size}
|
|
|
|
{
|
|
|
|
std::uninitialized_move(other.begin(), other.end(), begin());
|
|
|
|
other.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
static_vector(std::initializer_list<value_type> ilist) : m_size{static_cast<size_type>(ilist.size())}
|
|
|
|
{
|
|
|
|
assert(ilist.size() <= N);
|
|
|
|
std::uninitialized_copy(ilist.begin(), ilist.end(), begin());
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Iter, typename = std::enable_if_t<ranges::input_iterator<Iter>>>
|
|
|
|
static_vector(Iter first, Iter last) : static_vector(first, last, typename std::iterator_traits<Iter>::iterator_category{})
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
~static_vector() { std::destroy(begin(), end()); }
|
|
|
|
|
|
|
|
static_vector& operator=(const static_vector& rhs)
|
|
|
|
{
|
|
|
|
if (this != &rhs) {
|
|
|
|
if (m_size > rhs.m_size) {
|
|
|
|
std::destroy(begin() + rhs.m_size, end());
|
|
|
|
std::copy(rhs.begin(), rhs.end(), begin());
|
|
|
|
} else {
|
|
|
|
if constexpr (std::is_trivially_copy_assignable_v<value_type>
|
|
|
|
&& std::is_trivially_copy_constructible_v<value_type>) {
|
|
|
|
std::memcpy(&m_storage, &rhs.m_storage, rhs.m_size * sizeof(value_type));
|
|
|
|
} else {
|
|
|
|
std::copy(rhs.begin(), rhs.begin() + m_size, begin());
|
|
|
|
std::uninitialized_copy(rhs.begin() + m_size, rhs.end(), end());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_size = rhs.m_size;
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
static_vector& operator=(static_vector&& rhs) noexcept
|
|
|
|
{
|
|
|
|
if (this != &rhs) {
|
|
|
|
if (m_size > rhs.m_size) {
|
|
|
|
std::destroy(begin() + rhs.m_size, end());
|
|
|
|
std::move(rhs.begin(), rhs.end(), begin());
|
|
|
|
} else {
|
|
|
|
if constexpr (std::is_trivially_move_assignable_v<value_type>
|
|
|
|
&& std::is_trivially_move_constructible_v<value_type>) {
|
|
|
|
std::memcpy(&m_storage, &rhs.m_storage, rhs.m_size * sizeof(value_type));
|
|
|
|
} else {
|
|
|
|
std::copy(rhs.begin(), rhs.begin() + m_size, begin());
|
|
|
|
std::uninitialized_move(rhs.begin() + m_size, rhs.end(), end());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_size = rhs.m_size;
|
|
|
|
rhs.clear();
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
static_vector& operator=(std::initializer_list<value_type> rhs)
|
|
|
|
{
|
|
|
|
assert(rhs.size() <= N);
|
|
|
|
const size_type rhs_size = static_cast<size_type>(rhs.size());
|
|
|
|
if (m_size > rhs_size) {
|
|
|
|
std::destroy(begin() + rhs_size, end());
|
|
|
|
std::copy(rhs.begin(), rhs.end(), begin());
|
|
|
|
} else {
|
|
|
|
if constexpr (std::is_trivially_copy_assignable_v<value_type>
|
|
|
|
&& std::is_trivially_copy_constructible_v<value_type>) {
|
|
|
|
std::memcpy(&m_storage, rhs.begin(), rhs_size * sizeof(value_type));
|
|
|
|
} else {
|
|
|
|
std::copy(rhs.begin(), rhs.begin() + m_size, begin());
|
|
|
|
std::uninitialized_copy(rhs.begin() + m_size, rhs.end(), end());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_size = rhs_size;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
void assign(size_type count, const value_type& value)
|
|
|
|
{
|
|
|
|
assert(count <= N);
|
|
|
|
|
|
|
|
const pointer myfirst = begin();
|
|
|
|
const pointer mylast = end();
|
|
|
|
|
|
|
|
if (count > m_size) {
|
|
|
|
std::fill(myfirst, mylast, value);
|
|
|
|
|
|
|
|
std::uninitialized_fill_n(mylast, count - m_size, value);
|
|
|
|
} else {
|
|
|
|
const pointer newlast = myfirst + count;
|
|
|
|
std::fill(myfirst, newlast, value);
|
|
|
|
std::destroy(newlast, mylast);
|
|
|
|
}
|
|
|
|
m_size = count;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Iter, typename = std::enable_if_t<ranges::input_iterator<Iter>>>
|
|
|
|
void assign(Iter first, Iter last)
|
|
|
|
{
|
|
|
|
assign_range(first, last, typename std::iterator_traits<Iter>::iterator_category{});
|
|
|
|
}
|
|
|
|
|
|
|
|
void assign(std::initializer_list<T> ilist) { assign_range(ilist.begin(), ilist.end(), std::random_access_iterator_tag{}); }
|
|
|
|
|
|
|
|
reference at(size_type pos)
|
|
|
|
{
|
|
|
|
if (pos >= size()) std::cerr << "Invalid static_vector<T, N> subscript: Referring index is out of range!" << std::endl;
|
|
|
|
|
|
|
|
return *(data() + pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
const_reference at(size_type pos) const
|
|
|
|
{
|
|
|
|
if (pos >= size()) std::cerr << "Invalid static_vector<T, N> subscript: Referring index is out of range!" << std::endl;
|
|
|
|
|
|
|
|
return *(data() + pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
reference operator[](size_type pos) noexcept
|
|
|
|
{
|
|
|
|
assert(pos < size());
|
|
|
|
return *(data() + pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
const_reference operator[](size_type pos) const noexcept
|
|
|
|
{
|
|
|
|
assert(pos < size());
|
|
|
|
return *(data() + pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
reference front() noexcept
|
|
|
|
{
|
|
|
|
assert(!empty());
|
|
|
|
return *data();
|
|
|
|
}
|
|
|
|
|
|
|
|
const_reference front() const noexcept
|
|
|
|
{
|
|
|
|
assert(!empty());
|
|
|
|
return *data();
|
|
|
|
}
|
|
|
|
|
|
|
|
reference back() noexcept
|
|
|
|
{
|
|
|
|
assert(!empty());
|
|
|
|
return *(data() + m_size - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
const_reference back() const noexcept
|
|
|
|
{
|
|
|
|
assert(!empty());
|
|
|
|
return *(data() + m_size - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
pointer data() noexcept { return reinterpret_cast<pointer>(&m_storage); }
|
|
|
|
|
|
|
|
const_pointer data() const noexcept { return reinterpret_cast<const_pointer>(&m_storage); }
|
|
|
|
|
|
|
|
iterator begin() noexcept { return data(); }
|
|
|
|
|
|
|
|
const_iterator begin() const noexcept { return data(); }
|
|
|
|
|
|
|
|
const_iterator cbegin() const noexcept { return begin(); }
|
|
|
|
|
|
|
|
iterator end() noexcept { return data() + m_size; }
|
|
|
|
|
|
|
|
const_iterator end() const noexcept { return data() + m_size; }
|
|
|
|
|
|
|
|
const_iterator cend() const noexcept { return end(); }
|
|
|
|
|
|
|
|
reverse_iterator rbegin() noexcept { return reverse_iterator{end()}; }
|
|
|
|
|
|
|
|
const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator{end()}; }
|
|
|
|
|
|
|
|
const_reverse_iterator crbegin() const noexcept { return rbegin(); }
|
|
|
|
|
|
|
|
reverse_iterator rend() noexcept { return reverse_iterator{begin()}; }
|
|
|
|
|
|
|
|
const_reverse_iterator rend() const noexcept { return const_reverse_iterator{begin()}; }
|
|
|
|
|
|
|
|
const_reverse_iterator crend() const noexcept { return rend(); }
|
|
|
|
|
|
|
|
bool empty() const noexcept { return m_size == 0; }
|
|
|
|
|
|
|
|
size_type size() const noexcept { return m_size; }
|
|
|
|
|
|
|
|
size_type max_size() const noexcept { return N; }
|
|
|
|
|
|
|
|
size_type capacity() const noexcept { return N; }
|
|
|
|
|
|
|
|
void clear() noexcept
|
|
|
|
{
|
|
|
|
if constexpr (!std::is_trivially_destructible_v<T>) {
|
|
|
|
for (value_type& elem : *this) elem.~value_type();
|
|
|
|
}
|
|
|
|
m_size = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
iterator insert(const_iterator pos, const value_type& value) { return emplace(pos, value); }
|
|
|
|
|
|
|
|
iterator insert(const_iterator pos, value_type&& value) { return emplace(pos, std::move(value)); }
|
|
|
|
|
|
|
|
iterator insert(const_iterator pos, size_type count, const value_type& value)
|
|
|
|
{
|
|
|
|
assert(size() + count <= N);
|
|
|
|
iterator last = end();
|
|
|
|
assert(begin() <= pos && pos <= last);
|
|
|
|
const pointer posptr = const_cast<pointer>(pos);
|
|
|
|
|
|
|
|
const auto affected_elements = static_cast<size_type>(last - posptr);
|
|
|
|
|
|
|
|
if (affected_elements == 0)
|
|
|
|
;
|
|
|
|
else if (affected_elements < count) {
|
|
|
|
// [pos, last) --move--> [last, ...)
|
|
|
|
std::uninitialized_move(posptr, last, last);
|
|
|
|
std::destroy(posptr, last);
|
|
|
|
} else {
|
|
|
|
if constexpr (std::is_trivially_move_assignable_v<value_type>) {
|
|
|
|
if constexpr (std::is_trivially_move_constructible_v<value_type>)
|
|
|
|
// 1. + 2. [pos, last) --move--> [pos+count, last+count)
|
|
|
|
std::memmove(posptr + count, posptr, (last - posptr) * sizeof(value_type));
|
|
|
|
else {
|
|
|
|
// 1. [last-count, last) -> [last, last+count)
|
|
|
|
std::uninitialized_move(last - count, last, last);
|
|
|
|
// 2. [pos, last-count) --move--> [pos+count, last)
|
|
|
|
std::memmove(posptr + count, posptr, (last - posptr - count) * sizeof(value_type));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// 1. [last-count, last) -> [last, last+count)
|
|
|
|
std::uninitialized_move(last - count, last, last);
|
|
|
|
// 2. [pos, last-count) --move--> [pos+count, last)
|
|
|
|
for (auto cursor = last - count - 1; cursor >= pos; --cursor) *(cursor + count) = std::move(*cursor);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::destroy(posptr, posptr + count);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::uninitialized_fill(posptr, posptr + count, value);
|
|
|
|
|
|
|
|
m_size += count;
|
|
|
|
|
|
|
|
return posptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Iter, typename = std::enable_if_t<ranges::input_iterator<Iter>>>
|
|
|
|
iterator insert(const_iterator pos, Iter first, Iter last)
|
|
|
|
{
|
|
|
|
insert_range(pos, first, last, typename std::iterator_traits<Iter>::iterator_category{});
|
|
|
|
return const_cast<iterator>(pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
iterator insert(const_iterator pos, std::initializer_list<value_type> ilist)
|
|
|
|
{
|
|
|
|
return insert(pos, ilist.begin(), ilist.end());
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename... Args>
|
|
|
|
iterator emplace(const_iterator pos, Args&&... args)
|
|
|
|
{
|
|
|
|
pointer posptr = const_cast<pointer>(pos);
|
|
|
|
assert(size() < max_size());
|
|
|
|
iterator last = end();
|
|
|
|
assert(begin() <= pos && pos <= last);
|
|
|
|
|
|
|
|
if (posptr != last) {
|
|
|
|
if constexpr (std::is_trivially_move_assignable_v<value_type>) {
|
|
|
|
if constexpr (std::is_trivially_move_constructible_v<value_type>)
|
|
|
|
// 1. + 2. [pos, last) --move--> [pos+1, last+1)
|
|
|
|
std::memmove(posptr + 1, posptr, (last - posptr) * sizeof(value_type));
|
|
|
|
else {
|
|
|
|
// 1. pos -> last
|
|
|
|
new (last) value_type{std::move(*(last - 1))};
|
|
|
|
// 2. [pos, last-1) --move--> [pos+1, last)
|
|
|
|
std::memmove(posptr + 1, posptr, (last - posptr - 1) * sizeof(value_type));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// 1. pos -> last
|
|
|
|
new (last) value_type{std::move(*(last - 1))};
|
|
|
|
// 2. [pos, last-1) --move--> [pos+1, last)
|
|
|
|
for (auto cursor = last - 2; cursor >= pos; --cursor) *(cursor + 1) = std::move(*cursor);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 3. destroy
|
|
|
|
if constexpr (!std::is_trivially_destructible_v<value_type>) posptr->~value_type();
|
|
|
|
}
|
|
|
|
// 4. copy ctor at pos
|
|
|
|
new (posptr) value_type{std::forward<Args>(args)...};
|
|
|
|
|
|
|
|
++m_size;
|
|
|
|
|
|
|
|
return posptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
iterator erase(const_iterator pos) noexcept(std::is_nothrow_move_assignable_v<value_type>)
|
|
|
|
{
|
|
|
|
const pointer posptr = const_cast<pointer>(pos);
|
|
|
|
const pointer mylast = end();
|
|
|
|
assert(begin() <= pos && pos < mylast);
|
|
|
|
|
|
|
|
std::move(posptr + 1, mylast, posptr);
|
|
|
|
if constexpr (!std::is_trivially_destructible_v<value_type>) (mylast - 1)->~value_type();
|
|
|
|
m_size--;
|
|
|
|
return posptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
iterator erase(const_iterator first, const_iterator last) noexcept(std::is_nothrow_move_assignable_v<value_type>)
|
|
|
|
{
|
|
|
|
const pointer mylast = end();
|
|
|
|
assert(begin() <= first && first <= last && last <= mylast);
|
|
|
|
|
|
|
|
const pointer firstptr = const_cast<pointer>(first);
|
|
|
|
const pointer lastptr = const_cast<pointer>(last);
|
|
|
|
|
|
|
|
const size_type affected_elements = conver_size(static_cast<size_type>(last - first));
|
|
|
|
|
|
|
|
if (affected_elements > 0) {
|
|
|
|
const pointer newlast = std::move(lastptr, mylast, firstptr);
|
|
|
|
std::destroy(newlast, mylast);
|
|
|
|
m_size -= affected_elements;
|
|
|
|
}
|
|
|
|
|
|
|
|
return firstptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void push_back(const value_type& value)
|
|
|
|
{
|
|
|
|
assert(size() < max_size());
|
|
|
|
new (data() + m_size) value_type{value};
|
|
|
|
++m_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
void push_back(T&& value)
|
|
|
|
{
|
|
|
|
assert(size() < max_size());
|
|
|
|
new (data() + m_size) value_type{std::move(value)};
|
|
|
|
++m_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename... Args>
|
|
|
|
reference emplace_back(Args&&... args)
|
|
|
|
{
|
|
|
|
assert(size() < max_size());
|
|
|
|
pointer addr = data() + m_size;
|
|
|
|
new (addr) value_type{std::forward<Args>(args)...};
|
|
|
|
++m_size;
|
|
|
|
return *addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void pop_back()
|
|
|
|
{
|
|
|
|
assert(!empty());
|
|
|
|
if constexpr (!std::is_trivially_destructible_v<value_type>) back().~value_type();
|
|
|
|
--m_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
void resize(size_type count)
|
|
|
|
{
|
|
|
|
assert(count <= max_size());
|
|
|
|
|
|
|
|
if (count > m_size)
|
|
|
|
std::uninitialized_default_construct(end(), begin() + count);
|
|
|
|
else
|
|
|
|
std::destroy(begin() + count, end());
|
|
|
|
|
|
|
|
m_size = count;
|
|
|
|
}
|
|
|
|
|
|
|
|
void resize(size_type count, const T& value)
|
|
|
|
{
|
|
|
|
assert(count <= max_size());
|
|
|
|
|
|
|
|
if (count > m_size)
|
|
|
|
std::uninitialized_fill(end(), begin() + count, value);
|
|
|
|
else
|
|
|
|
std::destroy(begin() + count, end());
|
|
|
|
|
|
|
|
m_size = count;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
template <typename Iter, typename = std::enable_if_t<ranges::input_iterator<Iter>>>
|
|
|
|
static_vector(Iter first, Iter last, std::input_iterator_tag)
|
|
|
|
{
|
|
|
|
for (; first != last; ++first) emplace_back(*first);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Iter, typename = std::enable_if_t<ranges::input_iterator<Iter>>>
|
|
|
|
static_vector(Iter first, Iter last, std::forward_iterator_tag)
|
|
|
|
{
|
|
|
|
auto mylast = std::uninitialized_copy(first, last, begin());
|
|
|
|
m_size = conver_size(static_cast<size_type>(mylast - begin()));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Iter>
|
|
|
|
void assign_range(Iter first, Iter last, std::input_iterator_tag)
|
|
|
|
{ // assign input range [first, last)
|
|
|
|
const pointer myfirst = begin();
|
|
|
|
const pointer mylast = end();
|
|
|
|
|
|
|
|
pointer cursor = myfirst;
|
|
|
|
|
|
|
|
for (; first != last && cursor != mylast; ++first, ++cursor) *cursor = *first;
|
|
|
|
|
|
|
|
// Trim.
|
|
|
|
std::destroy(cursor, mylast);
|
|
|
|
m_size = cursor - myfirst;
|
|
|
|
|
|
|
|
// Append.
|
|
|
|
for (; first != last; ++first) emplace_back(*first);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Iter>
|
|
|
|
void assign_range(Iter first, Iter last, std::forward_iterator_tag)
|
|
|
|
{ // assign forward range [first, last)
|
|
|
|
const auto newsize = conver_size(static_cast<size_type>(std::distance(first, last)));
|
|
|
|
assert(newsize <= N);
|
|
|
|
const pointer myfirst = begin();
|
|
|
|
const pointer mylast = end();
|
|
|
|
|
|
|
|
if (newsize > m_size) {
|
|
|
|
// performance note: traversing [first, _Mid) twice
|
|
|
|
const Iter mid = std::next(first, static_cast<difference_type>(m_size));
|
|
|
|
std::copy(first, mid, myfirst);
|
|
|
|
std::uninitialized_copy(mid, last, mylast);
|
|
|
|
} else {
|
|
|
|
const pointer newlast = myfirst + newsize;
|
|
|
|
std::copy(first, last, myfirst);
|
|
|
|
std::destroy(newlast, mylast);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_size = newsize;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Iter>
|
|
|
|
void insert_range(const_iterator pos, Iter first, Iter last, std::input_iterator_tag)
|
|
|
|
{
|
|
|
|
assert(begin() <= pos && pos <= end());
|
|
|
|
|
|
|
|
if (first == last) return; // nothing to do, avoid invalidating iterators
|
|
|
|
|
|
|
|
pointer myfirst = begin();
|
|
|
|
const auto whereoff = static_cast<size_type>(pos - myfirst);
|
|
|
|
const auto oldsize = m_size;
|
|
|
|
|
|
|
|
for (; first != last; ++first) {
|
|
|
|
emplace_back(*first);
|
|
|
|
assert(m_size < max_size());
|
|
|
|
++m_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::rotate(myfirst + whereoff, myfirst + oldsize, end());
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename S>
|
|
|
|
static constexpr size_type conver_size(const S& s) noexcept
|
|
|
|
{
|
|
|
|
assert(s <= N);
|
|
|
|
return static_cast<size_type>(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Iter>
|
|
|
|
void insert_range(const_iterator pos, Iter first, Iter last, std::forward_iterator_tag)
|
|
|
|
{
|
|
|
|
// insert forward range [first, last) at pos
|
|
|
|
const pointer posptr = const_cast<pointer>(pos);
|
|
|
|
const auto count = conver_size(static_cast<size_type>(std::distance(first, last)));
|
|
|
|
|
|
|
|
assert(count + m_size <= N);
|
|
|
|
assert(begin() <= pos && pos <= end());
|
|
|
|
|
|
|
|
const pointer oldlast = end();
|
|
|
|
|
|
|
|
// Attempt to provide the strong guarantee for EmplaceConstructible failure.
|
|
|
|
// If we encounter copy/move construction/assignment failure, provide the basic guarantee.
|
|
|
|
// (For one-at-back, this provides the strong guarantee.)
|
|
|
|
|
|
|
|
const auto affected_elements = static_cast<size_type>(oldlast - posptr);
|
|
|
|
|
|
|
|
if (count < affected_elements) { // some affected elements must be assigned
|
|
|
|
if constexpr (std::is_trivially_move_assignable_v<value_type>) {
|
|
|
|
if constexpr (std::is_trivially_move_constructible_v<value_type>)
|
|
|
|
std::memmove(posptr + count, posptr, affected_elements * sizeof(value_type));
|
|
|
|
else {
|
|
|
|
/*mylast = */ std::uninitialized_move(oldlast - count, oldlast, oldlast);
|
|
|
|
std::memmove(posptr + count, posptr, (affected_elements - count) * sizeof(value_type));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*mylast = */ std::uninitialized_move(oldlast - count, oldlast, oldlast);
|
|
|
|
std::move(posptr, oldlast - count, posptr + count);
|
|
|
|
for (auto cursor = oldlast - count - 1; cursor >= posptr; --cursor) *(cursor + count) = std::move(*cursor);
|
|
|
|
}
|
|
|
|
std::destroy(posptr, posptr + count);
|
|
|
|
std::uninitialized_copy(first, last, posptr);
|
|
|
|
} else { // affected elements don't overlap before/after
|
|
|
|
const pointer relocated = posptr + count;
|
|
|
|
std::uninitialized_move(posptr, oldlast, relocated);
|
|
|
|
std::destroy(posptr, oldlast);
|
|
|
|
|
|
|
|
std::uninitialized_copy(first, last, posptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_size += count;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::aligned_storage_t<sizeof(T) * N, alignof(T)> m_storage;
|
|
|
|
size_type m_size;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename T, std::size_t N>
|
|
|
|
bool operator==(const static_vector<T, N>& lhs, const static_vector<T, N>& rhs)
|
|
|
|
{
|
|
|
|
return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, std::size_t N>
|
|
|
|
bool operator<(const static_vector<T, N>& lhs, const static_vector<T, N>& rhs)
|
|
|
|
{
|
|
|
|
return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, std::size_t N>
|
|
|
|
bool operator!=(const static_vector<T, N>& lhs, const static_vector<T, N>& rhs)
|
|
|
|
{
|
|
|
|
return !(lhs == rhs);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, std::size_t N>
|
|
|
|
bool operator>(const static_vector<T, N>& lhs, const static_vector<T, N>& rhs)
|
|
|
|
{
|
|
|
|
return rhs < lhs;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, std::size_t N>
|
|
|
|
bool operator<=(const static_vector<T, N>& lhs, const static_vector<T, N>& rhs)
|
|
|
|
{
|
|
|
|
return !(rhs < lhs);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, std::size_t N>
|
|
|
|
bool operator>=(const static_vector<T, N>& lhs, const static_vector<T, N>& rhs)
|
|
|
|
{
|
|
|
|
return !(lhs < rhs);
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace detail
|
|
|
|
{
|
|
|
|
template <size_t N>
|
|
|
|
struct static_vector_bind {
|
|
|
|
template <typename T>
|
|
|
|
using type = static_vector<T, N>;
|
|
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(pop)
|
|
|
|
#endif
|