template<class T, class D = void(*)(T*, std::size_t)>
Death::Containers::Array class

Array.

Template parameters
T Element type
D Deleter type. Defaults to pointer to a void(T*, std::size_t) function, where first is array pointer and second array size.

A RAII owning wrapper around a plain C array. A lighter alternative to std::vector that's deliberately move-only to avoid accidental copies of large memory blocks. For a variant with compile-time size information see StaticArray. A non-owning version of this container is an ArrayView.

The array has a non-changeable size by default and growing functionality is opt-in, see Growable arrays below for more information.

Usage

The Array class provides an access and slicing API similar to ArrayView, see its usage docs for details. All Array slicing APIs return an ArrayView, additionally Array instances are also implicitly convertible to it. The only difference is due to the owning aspect — mutable access to the data is provided only via non const overloads.

Array initialization

The array is by default value-initialized, which means that trivial types are zero-initialized and the default constructor is called on other types. It is possible to initialize the array in a different way using so-called tags:

Wrapping externally allocated arrays

By default the class makes all allocations using operator new[] and deallocates using operator delete[] for given T, with some additional trickery done internally to make the Array(NoInitT, std::size_t) and Array(DirectInitT, std::size_t, Args&&... args) constructors work. It's however also possible to wrap an externally allocated array using Array(T*, std::size_t, D) together with specifying which function to use for deallocation. By default the deleter is set to nullptr, which is equivalent to deleting the contents using operator delete[].

By default, plain function pointers are used to avoid having the type affected by the deleter function. If the deleter needs to manage some state, a custom deleter type can be used. The deleter is called unconditionally on destruction, which has some implications especially in case of stateful deleters. See the documentation of Array(T*, std::size_t, D) for details.

Growable arrays

The Array class provides no reallocation or growing capabilities on its own, and this functionality is opt-in via free functions from Containers/GrowableArray.h instead. This is done in order to keep the concept of an owning container decoupled from the extra baggage coming from custom allocators, type constructibility and such.

As long as the type stored in the array is nothrow-move-constructible, any Array instance can be converted to a growing container by calling the family of arrayAppend(), arrayInsert(), arrayReserve(), arrayResize(), arrayRemove() ... functions. A growable array behaves the same as a regular array to its consumers — its size() returns the count of real elements, while available capacity can be queried through arrayCapacity().

A growable array can be turned back into a regular one using arrayShrink() if desired. That'll free all extra memory, moving the elements to an array of exactly the size needed.

Growable allocators

Similarly to standard containers, growable arrays allow you to use a custom allocator that matches the documented semantics of ArrayAllocator. It's also possible to switch between different allocators during the lifetime of an Array instance — internally it's the same process as when a non-growable array is converted to a growable version (or back, with arrayShrink()).

The ArrayAllocator is by default aliased to ArrayNewAllocator, which uses the standard C++ new[] / delete[] constructs and is fully move-aware, requiring the types to be only nothrow-move-constructible at the very least. If a type is trivially copyable, the ArrayMallocAllocator will get picked instead, make use of std::realloc() to avoid unnecessary memory copies when growing the array. The typeless nature of ArrayMallocAllocator internals allows for free type-casting of the array instance with arrayAllocatorCast(), an operation not easily doable using typed allocators.

AddressSanitizer container annotations

Because the alloacted growable arrays have an area between size() and arrayCapacity() that shouldn't be accessed, when building with Address Sanitizer enabled, this area is marked as "container overflow".

In some cases sanitizer annotations are undesirable, for example when only a part of the application is built with AddressSanitizer enabled, causing false positives due to the annotations being done only partially, or when a particular platform is known to have broken behavior. The annotations can be disabled by defining DEATH_CONTAINERS_NO_SANITIZER_ANNOTATIONS on the compiler command line.

Conversion to array views

Arrays are implicitly convertible to ArrayView as described in the following table. The conversion is only allowed if T* is implicitly convertible to U* (or both are the same type) and both have the same size.

Owning array typeNon-owning view type
Array<T>ArrayView<U>
Array<T>ArrayView<const U>
const Array<T>ArrayView<const U>

Public types

using Type = T
Element type.
using Deleter = D
Deleter type.

Constructors, destructors, conversion operators

Array(std::nullptr_t = nullptr) noexcept
Default constructor.
Array(DefaultInitT, std::size_t size) explicit
Construct a default-initialized array.
Array(ValueInitT, std::size_t size) explicit
Construct a value-initialized array.
Array(NoInitT, std::size_t size) explicit
Construct an array without initializing its contents.
template<class ... Args>
Array(DirectInitT, std::size_t size, Args && ... args) explicit
Construct a direct-initialized array.
Array(InPlaceInitT, ArrayView<const T> list)
Construct a list-initialized array.
Array(InPlaceInitT, std::initializer_list<T> list)
Array(std::size_t size) explicit
Construct a value-initialized array.
Array(T* data, std::size_t size, D deleter = {}) explicit noexcept
Wrap an existing array with an explicit deleter.
Array(ArrayView<T> view, D deleter) explicit noexcept
Wrap an existing array view with an explicit deleter.
Array(const Array<T, D>&) deleted
Copying is not allowed.
Array(Array<T, D>&& other) noexcept
Move constructor.
~Array()
Destructor.
template<class U, class = decltype(Implementation::ArrayViewConverter<T, U>::to(std::declval<ArrayView<T>>()))>
operator U()
Convert to external view representation.
template<class U, class = decltype(Implementation::ArrayViewConverter<const T, U>::to(std::declval<ArrayView<const T>>()))>
operator U() const constexpr
operator bool() const explicit
Whether the array is non-empty.
operator T*() &
Conversion to array type.
operator const T*() const &

Public functions

auto operator=(const Array<T, D>&) -> Array<T, D>& deleted
Copying is not allowed.
auto operator=(Array<T, D>&& other) -> Array<T, D>& noexcept
Move assignment.
auto data() -> T*
Array data.
auto data() const -> const T*
auto deleter() const -> D
Array deleter.
auto size() const -> std::size_t
Array size.
auto empty() const -> bool
Whether the array is empty.
auto begin() -> T*
Pointer to first element.
auto begin() const -> const T*
auto cbegin() const -> const T*
auto end() -> T*
Pointer to (one item after) last element.
auto end() const -> const T*
auto cend() const -> const T*
auto front() -> T&
First element.
auto front() const -> const T&
auto back() -> T&
Last element.
auto back() const -> const T&
auto operator[](std::size_t i) -> T&
Element access.
auto operator[](std::size_t i) const -> const T&
auto slice(T* begin, T* end) -> ArrayView<T>
View on a slice.
auto slice(const T* begin, const T* end) const -> ArrayView<const T>
auto slice(std::size_t begin, std::size_t end) -> ArrayView<T>
auto slice(std::size_t begin, std::size_t end) const -> ArrayView<const T>
auto sliceSize(T* begin, std::size_t size) -> ArrayView<T>
View on a slice of given size.
auto sliceSize(const T* begin, std::size_t size) const -> ArrayView<const T>
auto sliceSize(std::size_t begin, std::size_t size) -> ArrayView<T>
auto sliceSize(std::size_t begin, std::size_t size) const -> ArrayView<const T>
template<std::size_t size_>
auto slice(T* begin) -> StaticArrayView<size_, T>
Fixed-size view on a slice.
template<std::size_t size_>
auto slice(const T* begin) const -> StaticArrayView<size_, const T>
template<std::size_t size_>
auto slice(std::size_t begin) -> StaticArrayView<size_, T>
template<std::size_t size_>
auto slice(std::size_t begin) const -> StaticArrayView<size_, const T>
template<std::size_t begin_, std::size_t end_>
auto slice() -> StaticArrayView<end_ - begin_, T>
Fixed-size view on a slice.
template<std::size_t begin_, std::size_t end_>
auto slice() const -> StaticArrayView<end_ - begin_, const T>
auto prefix(T* end) -> ArrayView<T>
View on a prefix until a pointer.
auto prefix(const T* end) const -> ArrayView<const T>
auto suffix(T* begin) -> ArrayView<T>
View on a suffix after a pointer.
auto suffix(const T* begin) const -> ArrayView<const T>
auto prefix(std::size_t end) -> ArrayView<T>
View on the first size items.
auto prefix(std::size_t end) const -> ArrayView<const T>
template<std::size_t viewSize_>
auto prefix() -> StaticArrayView<viewSize_, T>
Fixed-size view on the first size_ items.
template<std::size_t viewSize_>
auto prefix() const -> StaticArrayView<viewSize_, const T>
template<std::size_t size_>
auto suffix() -> StaticArrayView<size_, T>
Fixed-size view on the last size_ items.
template<std::size_t size_>
auto suffix() const -> StaticArrayView<size_, const T>
auto exceptPrefix(std::size_t size_) -> ArrayView<T>
View except the first size_ items.
auto exceptPrefix(std::size_t size_) const -> ArrayView<const T>
auto exceptSuffix(std::size_t size) -> ArrayView<T>
View except the last size items.
auto exceptSuffix(std::size_t size) const -> ArrayView<const T>
auto release() -> T*
Release data storage.

Typedef documentation

template<class T, class D>
typedef D Death::Containers::Array<T, D>::Deleter

Deleter type.

Defaults to pointer to a void(T*, std::size_t) function, where first is array pointer and second array size.

Function documentation

template<class T, class D>
Death::Containers::Array<T, D>::Array(std::nullptr_t = nullptr) noexcept

Default constructor.

Creates a zero-sized array. Move an Array with a nonzero size onto the instance to make it useful.

template<class T, class D>
Death::Containers::Array<T, D>::Array(DefaultInitT, std::size_t size) explicit

Construct a default-initialized array.

Creates an array of given size, the contents are default-initialized (i.e. trivial types are not initialized, default constructor called otherwise). If the size is zero, no allocation is done. Because of the differing behavior for trivial types it's better to explicitly use either the Array(ValueInitT, std::size_t) or the Array(NoInitT, std::size_t) variant instead.

template<class T, class D>
Death::Containers::Array<T, D>::Array(ValueInitT, std::size_t size) explicit

Construct a value-initialized array.

Creates an array of given size, the contents are value-initialized (i.e. trivial types are zero-initialized, default constructor called otherwise). This is the same as Array(std::size_t). If the size is zero, no allocation is done.

template<class T, class D>
Death::Containers::Array<T, D>::Array(NoInitT, std::size_t size) explicit

Construct an array without initializing its contents.

Creates an array of given size, the contents are not initialized. If the size is zero, no allocation is done. Useful if you will be overwriting all elements later anyway or if you need to call custom constructors in a way that's not expressible via any other Array constructor.

For trivial types is equivalent to Array(DefaultInitT, std::size_t), with deleter() being the default (nullptr) as well. For non-trivial types, the data are allocated as a char array. Destruction is done using a custom deleter that explicitly calls the destructor on all elements and then deallocates the data as a char array again — which means that for non-trivial types you're expected to construct all elements using placement new (or for example std::uninitialized_copy()) in order to avoid calling destructors on uninitialized memory.

template<class T, class D> template<class ... Args>
Death::Containers::Array<T, D>::Array(DirectInitT, std::size_t size, Args && ... args) explicit

Construct a direct-initialized array.

Allocates the array using the Array(NoInitT, std::size_t) constructor and then initializes each element with placement new using forwarded args.

template<class T, class D>
Death::Containers::Array<T, D>::Array(InPlaceInitT, ArrayView<const T> list)

Construct a list-initialized array.

Allocates the array using the Array(NoInitT, std::size_t) constructor and then copy-initializes each element with placement new using values from list. To save typing you can also use the array(ArrayView<const T>) / array(std::initializer_list<T>) shorthands.

template<class T, class D>
Death::Containers::Array<T, D>::Array(InPlaceInitT, std::initializer_list<T> list)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D>
Death::Containers::Array<T, D>::Array(std::size_t size) explicit

Construct a value-initialized array.

Alias to Array(ValueInitT, std::size_t).

template<class T, class D>
Death::Containers::Array<T, D>::Array(T* data, std::size_t size, D deleter = {}) explicit noexcept

Wrap an existing array with an explicit deleter.

The deleter will be unconditionally called on destruction with data and size as an argument. In particular, it will be also called if data is nullptr or size is 0.

In case of a moved-out instance, the deleter gets reset to a default-constructed value alongside the array pointer and size. For plain deleter function pointers it effectively means delete[] nullptr gets called when destructing a moved-out instance (which is a no-op), for stateful deleters you have to ensure the deleter similarly does nothing in its default state.

template<class T, class D>
Death::Containers::Array<T, D>::Array(ArrayView<T> view, D deleter) explicit noexcept

Wrap an existing array view with an explicit deleter.

Convenience overload of Array(T*, std::size_t, D) for cases where the pointer and size is already wrapped in an ArrayView, such as when creating non-owned Array instances.

template<class T, class D>
Death::Containers::Array<T, D>::Array(Array<T, D>&& other) noexcept

Move constructor.

Resets data pointer, size and deleter of other to be equivalent to a default-constructed instance.

template<class T, class D>
Death::Containers::Array<T, D>::~Array()

Destructor.

Calls deleter() on the owned data().

template<class T, class D> template<class U, class = decltype(Implementation::ArrayViewConverter<const T, U>::to(std::declval<ArrayView<const T>>()))>
Death::Containers::Array<T, D>::operator U() const constexpr

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D>
Death::Containers::Array<T, D>::operator const T*() const &

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D>
Array<T, D>& Death::Containers::Array<T, D>::operator=(Array<T, D>&& other) noexcept

Move assignment.

Swaps data pointer, size and deleter of the two instances.

template<class T, class D>
const T* Death::Containers::Array<T, D>::data() const

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D>
D Death::Containers::Array<T, D>::deleter() const

Array deleter.

If set to nullptr, the contents are deleted using standard operator delete[].

template<class T, class D>
const T* Death::Containers::Array<T, D>::begin() const

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D>
const T* Death::Containers::Array<T, D>::cbegin() const

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D>
const T* Death::Containers::Array<T, D>::end() const

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D>
const T* Death::Containers::Array<T, D>::cend() const

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D>
T& Death::Containers::Array<T, D>::front()

First element.

Expects there is at least one element.

template<class T, class D>
const T& Death::Containers::Array<T, D>::front() const

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D>
T& Death::Containers::Array<T, D>::back()

Last element.

Expects there is at least one element.

template<class T, class D>
const T& Death::Containers::Array<T, D>::back() const

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D>
T& Death::Containers::Array<T, D>::operator[](std::size_t i)

Element access.

Expects that i is less than size().

template<class T, class D>
const T& Death::Containers::Array<T, D>::operator[](std::size_t i) const

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D>
ArrayView<T> Death::Containers::Array<T, D>::slice(T* begin, T* end)

View on a slice.

Equivalent to ArrayView::slice(T*, T*) const and overloads.

template<class T, class D>
ArrayView<const T> Death::Containers::Array<T, D>::slice(const T* begin, const T* end) const

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D>
ArrayView<T> Death::Containers::Array<T, D>::slice(std::size_t begin, std::size_t end)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D>
ArrayView<const T> Death::Containers::Array<T, D>::slice(std::size_t begin, std::size_t end) const

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D>
ArrayView<T> Death::Containers::Array<T, D>::sliceSize(T* begin, std::size_t size)

View on a slice of given size.

Equivalent to ArrayView::sliceSize(T*, std::size_t) const and overloads.

template<class T, class D>
ArrayView<const T> Death::Containers::Array<T, D>::sliceSize(const T* begin, std::size_t size) const

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D>
ArrayView<T> Death::Containers::Array<T, D>::sliceSize(std::size_t begin, std::size_t size)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D>
ArrayView<const T> Death::Containers::Array<T, D>::sliceSize(std::size_t begin, std::size_t size) const

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D> template<std::size_t size_>
StaticArrayView<size_, T> Death::Containers::Array<T, D>::slice(T* begin)

Fixed-size view on a slice.

Equivalent to ArrayView::slice(T*) const and overloads.

template<class T, class D> template<std::size_t size_>
StaticArrayView<size_, const T> Death::Containers::Array<T, D>::slice(const T* begin) const

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D> template<std::size_t size_>
StaticArrayView<size_, T> Death::Containers::Array<T, D>::slice(std::size_t begin)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D> template<std::size_t size_>
StaticArrayView<size_, const T> Death::Containers::Array<T, D>::slice(std::size_t begin) const

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D> template<std::size_t begin_, std::size_t end_>
StaticArrayView<end_ - begin_, T> Death::Containers::Array<T, D>::slice()

Fixed-size view on a slice.

Equivalent to ArrayView::slice() const.

template<class T, class D> template<std::size_t begin_, std::size_t end_>
StaticArrayView<end_ - begin_, const T> Death::Containers::Array<T, D>::slice() const

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D>
ArrayView<T> Death::Containers::Array<T, D>::prefix(T* end)

View on a prefix until a pointer.

Equivalent to ArrayView::prefix(T*) const.

template<class T, class D>
ArrayView<const T> Death::Containers::Array<T, D>::prefix(const T* end) const

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D>
ArrayView<T> Death::Containers::Array<T, D>::suffix(T* begin)

View on a suffix after a pointer.

Equivalent to ArrayView::suffix(T*) const.

template<class T, class D>
ArrayView<const T> Death::Containers::Array<T, D>::suffix(const T* begin) const

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D>
ArrayView<T> Death::Containers::Array<T, D>::prefix(std::size_t end)

View on the first size items.

Equivalent to ArrayView::prefix(std::size_t) const.

template<class T, class D>
ArrayView<const T> Death::Containers::Array<T, D>::prefix(std::size_t end) const

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D> template<std::size_t viewSize_>
StaticArrayView<viewSize_, T> Death::Containers::Array<T, D>::prefix()

Fixed-size view on the first size_ items.

Equivalent to ArrayView::prefix() const.

template<class T, class D> template<std::size_t viewSize_>
StaticArrayView<viewSize_, const T> Death::Containers::Array<T, D>::prefix() const

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D> template<std::size_t size_>
StaticArrayView<size_, T> Death::Containers::Array<T, D>::suffix()

Fixed-size view on the last size_ items.

Equivalent to ArrayView::suffix() const.

template<class T, class D> template<std::size_t size_>
StaticArrayView<size_, const T> Death::Containers::Array<T, D>::suffix() const

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D>
ArrayView<T> Death::Containers::Array<T, D>::exceptPrefix(std::size_t size_)

View except the first size_ items.

Equivalent to ArrayView::exceptPrefix(std::size_t) const.

template<class T, class D>
ArrayView<const T> Death::Containers::Array<T, D>::exceptPrefix(std::size_t size_) const

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D>
ArrayView<T> Death::Containers::Array<T, D>::exceptSuffix(std::size_t size)

View except the last size items.

Equivalent to ArrayView::exceptSuffix().

template<class T, class D>
ArrayView<const T> Death::Containers::Array<T, D>::exceptSuffix(std::size_t size) const

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class T, class D>
T* Death::Containers::Array<T, D>::release()

Release data storage.

Returns the data pointer and resets data pointer, size and deleter to be equivalent to a default-constructed instance. Deleting the returned array is user responsibility — note the array might have a custom deleter() and so delete[] might not be always appropriate.

template<class T, class D> template<class T>
Array<T> array(ArrayView<const T> list)

Construct a list-initialized array.

Convenience shortcut to the Array::Array(InPlaceInitT, ArrayView<const T>) constructor. Not present as an implicit constructor in order to avoid the same usability issues as with std::vector.

template<class T, class D> template<class T>
Array<T> array(std::initializer_list<T> list)

Construct a list-initialized array.

Convenience shortcut to the Array::Array(InPlaceInitT, std::initializer_list<T>) constructor. Not present as an implicit constructor in order to avoid the same usability issues as with std::vector.