Skip to content

Commit 4e1e155

Browse files
Proposed specification for gsl::dyn_array (#2261)
* final draft of proposal * fix alloc formal * resolve ambiguity and fix TODO * some wording improvements * apply some feedback after offline review w/ @GabrielDosReis * Update specification after review from C++ Core Guidelines - Make sure iterators and references cannot be invalidated by any operations (besides destruction). - Update specification to annotate that some functions require certain language features (ranges and concepts). - Remove dependence on "Container" named requirements because they require iterator-invalidating behavior. - Explicitly add default and move constructors. - Fix some typos * Update dyn_array documentation reflecting editors' feedback Clarified that `gsl::dyn_array` cannot be moved or copied. Updated the FAQ section to emphasize that it is a fixed-size array without iterator/pointer-invalidating operations. * Update FAQ regarding gsl::dyn_array usage Clarified the purpose of gsl::dyn_array in the FAQ section. --------- Co-authored-by: Gabriel Dos Reis <[email protected]>
1 parent 851308a commit 4e1e155

File tree

1 file changed

+112
-0
lines changed

1 file changed

+112
-0
lines changed

docs/dyn_array.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
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

Comments
 (0)