В чём практическое следствие того, что размер std::array является частью его типа?
Размер std::array входит в тип: std::array<int, 3> и std::array<int, 4> — разные несовместимые типы. Поэтому размер известен на этапе компиляции, проверяется при передаче и присваивании, а изменить его во время выполнения нельзя.
Это даёт статическую проверку и отсутствие динамического выделения памяти, но уменьшает гибкость: контейнер с размером, известным только во время выполнения, нужно выбирать иначе, обычно std::vector или std::span для представления уже существующего диапазона.
Обычные массивы C имеют фиксированный размер, но плохо интегрированы с интерфейсами стандартной библиотеки: размер не является частью объекта, массивы часто неявно превращаются в указатели, а операций вроде size() и begin() у них нет.
std::array, появившийся в C++11, объединил преимущества встроенного массива — фиксированный размер и непрерывное размещение элементов — с интерфейсом контейнера STL. Размер оставили параметром шаблона, чтобы сохранить статическую информацию и не требовать динамической памяти.
Представим функцию, которая принимает массив ровно из восьми элементов. Если использовать указатель и отдельный размер, компилятор не проверит соответствие размера: можно передать указатель на меньший диапазон и получить выход за его границы.
Для std::array<T, 8> такое несоответствие выявляется типовой системой. Однако это же свойство запрещает присваивание контейнеров с разными размерами и не позволяет выбрать размер из пользовательского ввода во время выполнения.
Параметр N входит в специализацию шаблона. Поэтому std::array<int, 3> и std::array<int, 4> имеют разные типы, даже если совпадают тип элемента и фактическое содержимое.
Функция-шаблон может использовать размер как параметр компиляции: это позволяет проверять требования через static_assert, выбирать перегрузки и иногда оптимизировать обработку. При этом std::array хранит элементы непосредственно внутри объекта, поэтому сам контейнер не выполняет динамическое выделение памяти.
Размер нельзя изменить операциями push_back, resize или аналогичными: у std::array их нет. Если необходима последовательность переменного размера, применяется std::vector; если нужен только неп владеющий доступ к диапазону, удобен std::span в C++20.
Размер N влияет и на совместимость функций. Шаблон, принимающий std::array<T, N>, может отдельно ограничивать допустимый размер, тогда как функция, принимающая std::span<T>, обычно работает с диапазонами разных размеров, проверяя их длину уже во время выполнения.
Для std::array<T, 0> диапазон пуст: begin() равен end(). Обращение к элементу, а также вызов front() или back() для пустого массива недопустимы.
В протоколе устройства заголовок всегда содержит ровно 16 байт, а полезная нагрузка имеет переменный размер. Вариант с одним std::vector прост, но допускает изменение размера заголовка и не выражает его фиксированный формат на уровне типа.
Вариант с обычным массивом C экономичен, но хуже интегрирован с алгоритмами и интерфейсами контейнеров. Выбранное решение — std::array<std::byte, 16> для заголовка и std::vectorstd::byte для полезной нагрузки; это фиксирует контракт заголовка и сохраняет динамическую гибкость там, где она действительно нужна.
Если функция должна лишь читать оба диапазона единообразно, их можно представить через std::span. Такой интерфейс скрывает конкретный контейнер, но не заменяет статическую проверку точного размера, которую даёт параметр N.
Нет. Тип контейнера не определяет место размещения объекта. Локальный std::array обычно является автоматическим объектом, но его можно создать как часть объекта в динамической памяти, как статический объект или как поле другого объекта. Важно другое: элементы находятся внутри самого объекта, и контейнер не владеет отдельным динамически выделенным буфером.
Да, но интерфейс нужно выбрать осознанно. Параметр-шаблон std::array<T, N> сохраняет N и позволяет проверять его на этапе компиляции, но разные размеры порождают разные специализации функции. Параметр std::span<T> принимает диапазоны разных размеров через единый интерфейс, однако конкретная длина обычно проверяется во время выполнения; вариант std::span<T, N> сохраняет статический размер.
Заменить можно, но свойства будут другими. std::vector динамически управляет буфером, поддерживает изменение размера и обычно требует отдельного выделения памяти, тогда как std::array имеет размер и размещение, определённые типом, и не выполняет перераспределений. Для фиксированного небольшого объекта это делает std::array более точным выражением контракта и устраняет ненужную динамику; для размера, который определяется во время выполнения, std::vector остаётся подходящим выбором.