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

Array wrapper with size information.

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

Provides a RAII 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. It's usable in STL algorithms in the same way as a plain C array as well as in range-for cycles and other APIs operating with iterators. By default the array has a non-changeable size by default and growing functionality is opt-in, see Growable arrays below for more information. Usage example:

// Create default-initialized array with 5 integers and set them to some value
Containers::Array<int> a{5};
int b = 0;
for(auto& i: a) i = b++; // a = {0, 1, 2, 3, 4}

// Create array from given values
Containers::Array<int> c{Containers::InPlaceInit, {3, 18, -157, 0}};
c[3] = 25; // b = {3, 18, -157, 25}

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:

  • Array(DefaultInitT, std::size_t) leaves trivial types uninitialized and calls the default constructor elsewhere. In other words, new T[size].
  • Array(ValueInitT, std::size_t) is equivalent to the default case, zero-initializing trivial types and calling the default constructor elsewhere. Useful when you want to make the choice appear explicit. In other words, new T[size]{}.
  • Array(DirectInitT, std::size_t, Args&&... args) constructs all elements of the array using provided arguments. In other words, new T[size]{T{args...}, T{args...}, }.
  • Array(InPlaceInitT, std::initializer_list<T>) allocates unitialized memory and then copy-constructs all elements from the initializer list. In other words, new T[size]{args...}.
  • Array(NoInitT, std::size_t) does not initialize anything and you need to call the constructor on all elements manually using placement new, std::uninitialized_copy() or similar. This is the dangerous option. In other words, new char[size*sizeof(T)] for non-trivial types to circumvent default construction and new T[size] for trivial types.

Example:

// These are equivalent
Containers::Array<int> a1{5};
Containers::Array<int> a2{Containers::DefaultInit, 5};

// Array of 100 zeros
Containers::Array<int> b{Containers::ValueInit, 100};

// Array of type with no default constructor
struct Vec3 {
    explicit Vec3(float, float, float) {}
};
Containers::Array<Vec3> c{Containers::DirectInit, 5, 5.2f, 0.4f, 1.0f};

// Array from an initializer list
Containers::Array<int> d{Containers::InPlaceInit, {1, 2, 3, 4, -5, 0, 42}};

// Manual construction of each element
struct Foo {
    explicit Foo(int) {}
};
Containers::Array<Foo> e{Containers::NoInit, 5};
int index = 0;
for(Foo& f: e) new(&f) Foo(index++);

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. When wrapping an externally allocated array using Array(T*, std::size_t, D), it is possible to specify which function to use for deallocation. By default the deleter is set to nullptr, which is equivalent to deleting the contents using operator delete[].

For example, properly deallocating array allocated using std::malloc():

{
    int* data = reinterpret_cast<int*>(std::malloc(25*sizeof(int)));

    // Will call std::free() on destruction
    Containers::Array<int> array{data, 25,
        [](int* data, std::size_t) { std::free(data); }};
}

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:

class UnmapBuffer {
    public:
        explicit UnmapBuffer(GLuint id): _id{id} {}
        void operator()(char*, std::size_t) { glUnmapNamedBuffer(_id); }

    private:
        GLuint _id;
};

GLuint buffer;
char* data = reinterpret_cast<char*>(glMapNamedBuffer(buffer, GL_READ_WRITE));

// Will unmap the buffer on destruction
Containers::Array<char, UnmapBuffer> array{data, bufferSize, UnmapBuffer{buffer}};

Growable arrays

The Array class provides no reallocation or growing capabilities on its own, and this functionality is opt-in via free functions from Corrade/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(), arrayReserve(), arrayResize() ... 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(). Example of populating an array with an undetermined amount of elements:

/* Optimistically reserve assuming the model consists of just triangles */
Containers::Array<std::uint32_t> triangles;
Containers::arrayReserve(triangles, mesh.size()*3);
for(const Face& face: mesh) {
    /* If it's a quad, convert to two triangles */
    if(face.vertexCount == 4) Containers::arrayAppend(triangles,
        {face.vertices[0], face.vertices[1], face.vertices[2],
         face.vertices[0], face.vertices[2], face.vertices[3]});
    /* Otherwise add as-is */
    else Containers::arrayAppend(triangles,
        {face.vertices[0], face.vertices[1], face.vertices[2]});
}

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". Given the following code, ASan aborts and produces a failure report similar to the one below:

Containers::Array<int> a;
arrayReserve(a, 100);
arrayResize(a, 80);
a[80] = 5; // Even though the memory is there, this causes ASan to complain
SUMMARY: AddressSanitizer: container-overflow main.cpp:25:15 in main()
Shadow bytes around the buggy address:
  0x0c287fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff8000: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x0c287fff8010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff8020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c287fff8030: 00[fc]fc fc fc fc fc fc fc fc fc fa fa fa fa fa
  0x0c287fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fff8080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  …
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe

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. This also extends to other container types constructibe from ArrayView, which means for example that a StridedArrayView1D is implicitly convertible from Array as well.

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

STL compatibility

On compilers that support C++2a and std::span, implicit conversion of an Array to it is provided in Corrade/Containers/ArrayViewStlSpan.h. The conversion is provided in a separate header to avoid unconditional #include <span>, which significantly affects compile times. The following table lists allowed conversions:

Corrade typeSTL type
Array<T>std::span<T>
Array<T>std::span<const T>
const Array<T>std::span<const T>

There are some dangerous corner cases due to the way std::span is designed, see ArrayView STL compatibility for more information.

Public types

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

Constructors, destructors, conversion operators

Array(std::nullptr_t) noexcept
Conversion from nullptr.
Array() 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, std::initializer_list<T> list) explicit
Construct a list-initialized array.
Array(std::size_t size) explicit
Construct a value-initialized array.
Array(T* data, std::size_t size, D deleter = Implementation::DefaultDeleter<D>{}()) explicit
Wrap an existing array.
~Array()
Destructor.
Array(const Array<T, D>&) deleted
Copying is not allowed.
Array(Array<T, D>&& other) noexcept
Move constructor.
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 slice(T* begin, T* end) -> ArrayView<T>
Reference to array 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>
template<std::size_t size>
auto slice(T* begin) -> StaticArrayView<size, T>
Fixed-size array 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> new in 2019.10
Fixed-size array slice.
template<std::size_t begin_, std::size_t end_>
auto slice() const -> StaticArrayView<end_ - begin_, const T> new in 2019.10
auto prefix(T* end) -> ArrayView<T>
Array prefix.
auto prefix(const T* end) const -> ArrayView<const T>
auto prefix(std::size_t end) -> ArrayView<T>
auto prefix(std::size_t end) const -> ArrayView<const T>
template<std::size_t viewSize>
auto prefix() -> StaticArrayView<viewSize, T>
template<std::size_t viewSize>
auto prefix() const -> StaticArrayView<viewSize, const T>
auto suffix(T* begin) -> ArrayView<T>
Array suffix.
auto suffix(const T* begin) const -> ArrayView<const T>
auto suffix(std::size_t begin) -> ArrayView<T>
auto suffix(std::size_t begin) const -> ArrayView<const T>
auto except(std::size_t count) -> ArrayView<T> new in 2019.10
Array prefix except the last count items.
auto except(std::size_t count) const -> ArrayView<const T> new in 2019.10
auto release() -> T*
Release data storage.

Function documentation

template<class T, class D>
Corrade::Containers::Array<T, D>::Array() 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>
Corrade::Containers::Array<T, D>::Array(DefaultInitT, std::size_t size) explicit

Construct a default-initialized array.

Creates array of given size, the contents are default-initialized (i.e. builtin types are not initialized). If the size is zero, no allocation is done.

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

Construct a value-initialized array.

Creates array of given size, the contents are value-initialized (i.e. builtin types are zero-initialized). For other than builtin types this is the same as Array(std::size_t). If the size is zero, no allocation is done.

Useful if you want to create an array of primitive types and set them to zero.

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

Construct an array without initializing its contents.

Creates array of given size, the contents are not initialized. If the size is zero, no allocation is done. Initialize the values using placement new. Useful if you will be overwriting all elements later anyway.

  • For non-trivial types, the data are allocated as a char array. Destruction is done using a custom deleter that explicitly calls destructor on all elements (regardless of whether they were properly constructed or not) and then deallocates the data as a char array again.
  • For trivial types is equivalent to Array(DefaultInitT, std::size_t), with deleter() being the default (nullptr) as well. This is done in order to avoid needless problems with dangling custom deleters when returning arrays from dynamically loaded libraries.

template<class T, class D> template<class... Args>
Corrade::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>
Corrade::Containers::Array<T, D>::Array(InPlaceInitT, std::initializer_list<T> list) explicit

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.

Note that there's no plain initializer-list constructor because it would cause a similar fiasco as with std::vector, where std::vector<int>{5} creates one-element vector but std::vector<int>(5) creates a zero-initialized five-element vector. To save typing, you can use the array(std::initializer_list<T>) helper which doesn't suffer from this problem.

template<class T, class D>
Corrade::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>
Corrade::Containers::Array<T, D>::Array(T* data, std::size_t size, D deleter = Implementation::DefaultDeleter<D>{}()) explicit

Wrap an existing array.

Note that the array will be deleted on destruction using given deleter. See class documentation for more information about custom deleters and ArrayView for non-owning array wrapper.

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

Destructor.

Calls deleter() on the owned data().

template<class T, class D>
Corrade::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> template<class U, class = decltype(Implementation::ArrayViewConverter<T, U>::to(std::declval<ArrayView<T>>()))>
Corrade::Containers::Array<T, D>::operator U()

Convert to external view representation.

template<class T, class D> template<class U, class = decltype(Implementation::ArrayViewConverter<const T, U>::to(std::declval<ArrayView<const T>>()))>
Corrade::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>
Corrade::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>& Corrade::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* Corrade::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 Corrade::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>
T* Corrade::Containers::Array<T, D>::begin()

Pointer to first element.

template<class T, class D>
const T* Corrade::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* Corrade::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>
T* Corrade::Containers::Array<T, D>::end()

Pointer to (one item after) last element.

template<class T, class D>
const T* Corrade::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* Corrade::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& Corrade::Containers::Array<T, D>::front()

First element.

Expects there is at least one element.

template<class T, class D>
const T& Corrade::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& Corrade::Containers::Array<T, D>::back()

Last element.

Expects there is at least one element.

template<class T, class D>
const T& Corrade::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>
ArrayView<T> Corrade::Containers::Array<T, D>::slice(T* begin, T* end)

Reference to array slice.

Equivalent to ArrayView::slice().

template<class T, class D>
ArrayView<const T> Corrade::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> Corrade::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> Corrade::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> template<std::size_t size>
StaticArrayView<size, T> Corrade::Containers::Array<T, D>::slice(T* begin)

Fixed-size array slice.

Both begin and begin + size are expected to be in range.

template<class T, class D> template<std::size_t size>
StaticArrayView<size, const T> Corrade::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> Corrade::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> Corrade::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> Corrade::Containers::Array<T, D>::slice() new in 2019.10

Fixed-size array slice.

At compile time expects that begin < end_, at runtime that end_ is not larger than size().

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

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> Corrade::Containers::Array<T, D>::prefix(T* end)

Array prefix.

Equivalent to ArrayView::prefix().

template<class T, class D>
ArrayView<const T> Corrade::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> Corrade::Containers::Array<T, D>::prefix(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> Corrade::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> Corrade::Containers::Array<T, D>::prefix()

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, const T> Corrade::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>
ArrayView<T> Corrade::Containers::Array<T, D>::suffix(T* begin)

Array suffix.

Equivalent to ArrayView::suffix().

template<class T, class D>
ArrayView<const T> Corrade::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> Corrade::Containers::Array<T, D>::suffix(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>
ArrayView<const T> Corrade::Containers::Array<T, D>::suffix(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>
ArrayView<T> Corrade::Containers::Array<T, D>::except(std::size_t count) new in 2019.10

Array prefix except the last count items.

Equivalent to ArrayView::except().

template<class T, class D>
ArrayView<const T> Corrade::Containers::Array<T, D>::except(std::size_t count) const new in 2019.10

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* Corrade::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(std::initializer_list<T> list) new in 2020.06

Construct a list-initialized array.

Convenience shortcut to the Array::Array(InPlaceInitT, std::initializer_list<T>) constructor. See its documentation for a design rationale.

template<class T, class D> template<class U, class T, class D>
ArrayView<U> arrayCast(Array<T, D>& array)

Reinterpret-cast an array.

See arrayCast(ArrayView<T>) for more information.

template<class T, class D> template<class T>
std::size_t arraySize(const Array<T>& view)

Array size.

See arraySize(ArrayView<T>) for more information.