|
| 1 | +<!-- $ pandoc -V geometry:margin=1in -V colorlinks=true -o dyn_array.pdf dyn_array.md --> |
| 2 | + |
| 3 | +# `gsl::dyn_array<T, Allocator>` |
| 4 | + |
| 5 | +`gsl::dyn_array` is a dynamic array that is intended to be a replacement for slinging |
| 6 | +around raw pointers and sizes. Notably, it _owns_ all of its elements. It should replace |
| 7 | +the following idioms: |
| 8 | + |
| 9 | +* Replace `new T[n]` with `gsl::dyn_array<T>(n)`. |
| 10 | +* Replace both `foo(T*, size_t)` and `foo(unique_ptr<T[]>&, size_t)` with |
| 11 | +`foo(gsl::dyn_array<T>&)`. |
| 12 | + |
| 13 | +It can be thought of like a... |
| 14 | + |
| 15 | +* `std::array` except the size is specified at runtime. |
| 16 | +* `std::vector` except it can neither shrink nor grow. |
| 17 | + |
| 18 | +By design, `gsl::dyn_array` is not a `Container` as defined by the C++ Named |
| 19 | +Requirements because we want to avoid the invalidation of iterators or references to |
| 20 | +`gsl::dyn_array` objects. Furthermore, by design, there is no support for operations |
| 21 | +(other than destruction) that can invalidate iterators or pointers into the sequence |
| 22 | +owned by a `gsl::dyna_array` object. An `gsl::dyn_array` object cannot be moved, |
| 23 | +nor can it be copied. |
| 24 | + |
| 25 | +### Construction |
| 26 | +`gsl::dyn_array`s can be constructed in the following ways: |
| 27 | + |
| 28 | +* Default construct a `dyn_array` with no elements: |
| 29 | +```c++ |
| 30 | +constexpr dyn_array(); |
| 31 | +``` |
| 32 | + |
| 33 | +* Move construct a `dyn_array` from `other`: not possible |
| 34 | +```c++ |
| 35 | +dyn_array(dyn_array&& other) = delete; |
| 36 | +``` |
| 37 | +
|
| 38 | +* Construct a `dyn_array` with `n` default constructed elements: |
| 39 | +```c++ |
| 40 | +constexpr explicit dyn_array(size_t n, const Allocator & alloc = Allocator()); |
| 41 | +``` |
| 42 | + |
| 43 | +* Construct a `dyn_array` with `n` elements, each copy constructed from `arg`: |
| 44 | +```c++ |
| 45 | +constexpr dyn_array(size_t n, const T& arg, const Allocator & alloc = Allocator()); |
| 46 | +``` |
| 47 | +
|
| 48 | +* Construct a `dyn_array` with elements from the range `[first, last)`: |
| 49 | +```c++ |
| 50 | +template <typename InputIt> |
| 51 | +#ifdef __cpp_lib_concepts |
| 52 | + requires(std::input_iterator<InputIt>) |
| 53 | +#endif /* __cpp_lib_concepts */ |
| 54 | +constexpr dyn_array(InputIt first, InputIt last, const Allocator & alloc = Allocator()); |
| 55 | +``` |
| 56 | + |
| 57 | +* Construct a `dyn_array` from a range: |
| 58 | +```c++ |
| 59 | +#ifdef __cpp_lib_containers_range |
| 60 | +template <typename R> |
| 61 | + requires(std::ranges::range<R>) |
| 62 | +constexpr dyn_array(std::from_range_t, R&& r, const Allocator & alloc = Allocator()); |
| 63 | +#endif /* __cpp_lib_containers_range */ |
| 64 | +``` |
| 65 | + |
| 66 | +* Construct a `dyn_array` with elements from the initializer list: |
| 67 | +```c++ |
| 68 | +constexpr dyn_array(std::initializer_list<T>, const Allocator & alloc = Allocator()); |
| 69 | +``` |
| 70 | +
|
| 71 | +### Operations |
| 72 | +In addition to the operations required by the named requirements, `gsl::dyn_array` will |
| 73 | +support the following operations: |
| 74 | +
|
| 75 | +* Access the specified element **_with bounds checking_**: |
| 76 | +```c++ |
| 77 | +constexpr T& operator[](size_t); |
| 78 | +constexpr const T& operator[](size_t) const; |
| 79 | +``` |
| 80 | + |
| 81 | +* Access the underlying array: |
| 82 | +```c++ |
| 83 | +constexpr T* data() noexcept; |
| 84 | +constexpr const T* data() const noexcept; |
| 85 | +``` |
| 86 | + |
| 87 | +* Return the number of elements in the `dyn_array`: |
| 88 | +```c++ |
| 89 | +constexpr size_t size() const noexcept; |
| 90 | +``` |
| 91 | + |
| 92 | +### FAQ |
| 93 | + |
| 94 | +#### Why no push_back (and friends)? |
| 95 | +`gsl::dyn_array` is intended to be a fixed-size array and all objects should be |
| 96 | +constructed at creation. It supports no iterator/pointer-invalidating operation. |
| 97 | + |
| 98 | +#### Why does `gsl::dyn_array` not conform to the `Container` Named Requirements? |
| 99 | +`gsl::dyn_array` is intended to be a safer replacement for raw pointers and sizes. We |
| 100 | +don't want users to accidentally use it in a way that would be unsafe. For example, |
| 101 | +`gsl::dyn_array` does not have copy or move operations. This is because it |
| 102 | +would be possible to invalidate existing iterators and references. |
| 103 | + |
| 104 | +### Bounds Checking Semantics |
| 105 | +If an out-of-bounds access (read or write) is attempted, `gsl::dyn_array` should follow |
| 106 | +the contract violation strategy outlined in [GSL.assert: Assertions](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#gslassert-assertions). |
| 107 | + |
| 108 | +### References |
| 109 | +* [C++ Named Requirements](https://en.cppreference.com/w/cpp/named_req) |
| 110 | +* [Microsoft/GSL #1169](https://github.com/microsoft/GSL/issues/1169) |
| 111 | +* [isocpp/CppCoreGuidelines #2244](https://github.com/isocpp/CppCoreGuidelines/issues/2244) |
| 112 | +* [n3662](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3662.html) |
0 commit comments