Files
CppCoreGuidelines/docs/dyn_array.md
Carson Radtke 4e1e155fc4 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 <GabrielDosReis@users.noreply.github.com>
2025-12-03 11:25:33 -08:00

4.0 KiB

gsl::dyn_array<T, Allocator>

gsl::dyn_array is a dynamic array that is intended to be a replacement for slinging around raw pointers and sizes. Notably, it owns all of its elements. It should replace the following idioms:

  • Replace new T[n] with gsl::dyn_array<T>(n).
  • Replace both foo(T*, size_t) and foo(unique_ptr<T[]>&, size_t) with foo(gsl::dyn_array<T>&).

It can be thought of like a...

  • std::array except the size is specified at runtime.
  • std::vector except it can neither shrink nor grow.

By design, gsl::dyn_array is not a Container as defined by the C++ Named Requirements because we want to avoid the invalidation of iterators or references to gsl::dyn_array objects. Furthermore, by design, there is no support for operations (other than destruction) that can invalidate iterators or pointers into the sequence owned by a gsl::dyna_array object. An gsl::dyn_array object cannot be moved, nor can it be copied.

Construction

gsl::dyn_arrays can be constructed in the following ways:

  • Default construct a dyn_array with no elements:
constexpr dyn_array();
  • Move construct a dyn_array from other: not possible
dyn_array(dyn_array&& other) = delete;
  • Construct a dyn_array with n default constructed elements:
constexpr explicit dyn_array(size_t n, const Allocator & alloc = Allocator());
  • Construct a dyn_array with n elements, each copy constructed from arg:
constexpr dyn_array(size_t n, const T& arg, const Allocator & alloc = Allocator());
  • Construct a dyn_array with elements from the range [first, last):
template <typename InputIt>
#ifdef __cpp_lib_concepts
    requires(std::input_iterator<InputIt>)
#endif /* __cpp_lib_concepts */
constexpr dyn_array(InputIt first, InputIt last, const Allocator & alloc = Allocator());
  • Construct a dyn_array from a range:
#ifdef __cpp_lib_containers_range
template <typename R>
    requires(std::ranges::range<R>)
constexpr dyn_array(std::from_range_t, R&& r, const Allocator & alloc = Allocator());
#endif /* __cpp_lib_containers_range */
  • Construct a dyn_array with elements from the initializer list:
constexpr dyn_array(std::initializer_list<T>, const Allocator & alloc = Allocator());

Operations

In addition to the operations required by the named requirements, gsl::dyn_array will support the following operations:

  • Access the specified element with bounds checking:
constexpr T& operator[](size_t);
constexpr const T& operator[](size_t) const;
  • Access the underlying array:
constexpr T* data() noexcept;
constexpr const T* data() const noexcept;
  • Return the number of elements in the dyn_array:
constexpr size_t size() const noexcept;

FAQ

Why no push_back (and friends)?

gsl::dyn_array is intended to be a fixed-size array and all objects should be constructed at creation. It supports no iterator/pointer-invalidating operation.

Why does gsl::dyn_array not conform to the Container Named Requirements?

gsl::dyn_array is intended to be a safer replacement for raw pointers and sizes. We don't want users to accidentally use it in a way that would be unsafe. For example, gsl::dyn_array does not have copy or move operations. This is because it would be possible to invalidate existing iterators and references.

Bounds Checking Semantics

If an out-of-bounds access (read or write) is attempted, gsl::dyn_array should follow the contract violation strategy outlined in GSL.assert: Assertions.

References