В C++ какой смысл в std::array, если размер нужно определять во время компиляции?

Простите мое невежество, мне кажется, что std::array предназначен для замены STL ваших обычных массивов. Но поскольку размер массива должен быть передан в качестве параметра шаблона, это не позволяет нам создать std::array с размером, известным только во время выполнения.

std::array<char,3> nums {1,2,3}; // Works.

constexpr size_t size = 3;
std::array<char,size> nums {1,2,3}; // Works.

const buf_size = GetSize();
std::array<char, buf_size> nums; // Doesn't work.

Я бы предположил, что одним из очень важных вариантов использования массива в C++ является создание структуры данных фиксированного размера на основе входных данных времени выполнения (скажем, выделение буфера для чтения файлов).

Обходные пути, которые я использую для этого:

// Create a array pointer for on-the-spot usecases like reading from a file.
char *data = new char[size];
...
delete[] data;

or:

// Use unique_ptr as a class member and I don't want to manage the memory myself.
std::unique_ptr<char[]> myarr_ = std::unique_ptr<char[]>(new char[size]);

Если меня не волнует фиксированный размер, я знаю, что могу использовать std::vector<char> с предварительно определенным размером следующим образом:

std::vector<char> my_buf (buf_size);

Почему разработчики std::array решили проигнорировать этот вариант использования? Возможно, я не понимаю реального варианта использования std::array.

РЕДАКТИРОВАТЬ: Я думаю, мой вопрос можно сформулировать и другим способом: почему дизайнеры решили, что размер передается как параметр шаблона, а не как параметр конструктора? Не затруднит ли выбор последнего функционал, который сейчас есть у std::array? Мне кажется, что это преднамеренный выбор дизайна, и я не понимаю, почему.


person Plasty Grove    schedule 15.02.2020    source источник
comment
Какой вариант использования они упустили? Вы представили два, и в стандартной библиотеке есть покрытие для обоих.   -  person Ted Lyngmo    schedule 15.02.2020
comment
Если размер массива определяется во время выполнения, между этим и std::vector нет принципиальной разницы. Потому что это и есть std::vector.   -  person Sam Varshavchik    schedule 15.02.2020
comment
@TedLyngmo — коллекция фиксированного размера, размер которой известен только во время выполнения.   -  person Plasty Grove    schedule 15.02.2020
comment
@SamVarshavchik - Да, но std::vector не является фиксированным размером.   -  person Plasty Grove    schedule 15.02.2020
comment
@PlastyGrove Как это пропустили? Это именно то, что предлагает std::array.   -  person Ted Lyngmo    schedule 15.02.2020
comment
Он хочет именно std::dynarray open-std.org/ JTC1/sc22/WG21/docs/papers/2013/n3662.html   -  person Brandon    schedule 15.02.2020
comment
Итак, вы хотите знать, почему нет контейнера типа массива с фиксированным размером во время выполнения? Или вы хотите знать, каковы преимущества std::array с фиксированным размером во время компиляции?   -  person Galik    schedule 15.02.2020
comment
@TedLyngmo - Мои плохие толстые пальцы поправили. Я имел в виду коллекцию фиксированного размера, размер которой известен во время выполнения.   -  person Plasty Grove    schedule 15.02.2020
comment
@ Брэндон Ах ... это бесполезно в C ++, не так ли? Какова цель? (с риском попасть на территорию мнений)   -  person Ted Lyngmo    schedule 15.02.2020
comment
Просто представьте, что размер std::vector не может быть изменен после его создания. Проблема решена.   -  person Sam Varshavchik    schedule 15.02.2020
comment
@TedLyngmo понятия не имею .. но я думаю, он может использовать alloca или variable length array .. Это сделает то же самое. Возможно, он хочет, чтобы массив был выделен в стеке, а не в куче, но с размером времени выполнения.   -  person Brandon    schedule 15.02.2020
comment
@SamVarshavchik - я делаю вид, что его нельзя часто менять, и я использую std::vector. Но я не был уверен, что упускаю что-то фундаментальное в std::array.   -  person Plasty Grove    schedule 15.02.2020


Ответы (3)


Простота программирования

std::array поддерживает несколько полезных интерфейсов и идиом, которые используются в std::vector. С обычными массивами в стиле C нельзя иметь .size() (без взлома sizeof), .at() (исключение вне диапазона), front()/back(), итераторы и т.д. Все должно быть закодировано вручную.

Многие программисты могут выбрать std::vector даже для массивов известного размера во время компиляции, просто потому, что они хотят использовать вышеуказанные методологии программирования. Но это снижает производительность, доступную для массивов фиксированного размера во время компиляции.
Следовательно, std::array было предоставлено создателями библиотеки, чтобы препятствовать использованию массивов в стиле C, и при этом избегать std::vector, когда размер известен во время компиляции.

person iammilind    schedule 15.02.2020
comment
Но для разработчиков std::array было бы действительно так сложно иметь все функции, которые вы указали выше, И чтобы параметр размера передавался в конструктор, а не в качестве параметра шаблона? Талантливым разработчикам C++, стоящим за STL, это кажется тривиальным, но они решили этого не делать. Почему? - person Plasty Grove; 15.02.2020
comment
@PlastyGrove, если конструктору передается параметр размера, то это std::vector :-). В C у них есть что-то похожее, известное как вариационные массивы. Если размер известен только во время выполнения, оптимизатор компилятора не может в полной мере воспользоваться преимуществами создания этой переменной в памяти стека, что более эффективно. Вместо этого размер среды выполнения должен быть передан в свободное хранилище (память кучи) для создания массива среды выполнения. Это незначительное снижение производительности без уважительной причины. std::array — это просто оболочка для фиксированных массивов в стиле C. для обеспечения безопасности типов с помощью полезных интерфейсов. - person iammilind; 15.02.2020
comment
Понятно, значит, нет возможности создать массив в стеке, если я не передам параметр как параметр шаблона во время компиляции? - person Plasty Grove; 15.02.2020
comment
Распределение стека подразумевает, что данные для массива хранятся в самом объекте. Но это означало бы, что sizeof(runtime_sized_array) не может быть константой времени компиляции, что нарушило бы многие вещи. Например, как бы вы разместили в стеке std::optional<runtime_sized_array<int>>? Как бы вы реализовали std::vector<runtime_sized_array<int>>? Или даже runtime_sized_array<int> two_arrays[2]{ runtime_sized_array<int>(5), runtime_sized_array<int>(7) }? Если бы у вас был runtime_sized_array<int>* p, что бы сделал p++? - person Raymond Chen; 15.02.2020
comment
@RaymondChen - Это имеет смысл, спасибо за объяснение. - person Plasty Grove; 15.02.2020
comment
@PlastyGrove, массив в памяти стека создается только в том случае, если размер известен во время компиляции. В этом отношении в памяти стека создается все, чей размер является детерминированным (то есть известным заранее). Для размеров времени выполнения существует куча памяти (свободное хранилище). - person iammilind; 15.02.2020
comment
Вы упустили главное преимущество std::array (и вектора): они Обычные типы (для обычных типов элементов) - person Caleth; 08.09.2020
comment
Вполне возможно выделить std::vector в стеке с помощью специального распределителя. - person Sebastian Hoffmann; 08.09.2020

Я понимаю две основные причины:

  • std::array реализует интерфейсы STL для типов коллекций, позволяя передавать std::array как есть функциям и методам, которые принимают любой итератор STL.
  • Чтобы предотвратить распад указателя массива... (ниже)

... это сохранение информации о типе через границы функций/методов, потому что это предотвращает распад указателя массива.

Имея голый массив C/C++, вы можете передать его другой функции в качестве аргумента параметра четырьмя способами:

void by_value1   ( const T* array )
void by_value2   ( const T array[] )
void by_pointer  ( const T (*array)[U] )
void by_reference( const T (&array)[U] )
  • by_value1 и by_value2 семантически идентичны и вызывают разрушение указателя, поскольку принимающая функция не знает sizeof массива.
  • by_pointer и by_reference оба требуют U известной константы времени компиляции, но сохраняют sizeof информацию.

Таким образом, если вы избежите распада массива с помощью by_pointer или by_reference, у вас теперь возникнет проблема с обслуживанием, каждый раз, когда вы изменяете размер массива, вам придется вручную обновлять все сайты вызовов, которые имеют этот размер в U.

Используя std::array, вы позаботитесь об этом, сделав эти функции template функциями, где U является параметром (конечно, вы можете по-прежнему использовать методы by_pointer и by_reference, но с более запутанным синтаксисом).

... поэтому std::array добавляет 5-й способ:

template<typename T, size_t N>
void by_stdarray( const std::array<T,N>& array )
person Dai    schedule 15.02.2020

std::array является заменой массивов в стиле C.

Стандарты C++ не позволяют объявлять массивы в стиле C без размеров, определенных во время компиляции.

person Sid S    schedule 15.02.2020
comment
std::array - это не замена, а оболочка вокруг массива в стиле C. Массив в стиле C — это языковая функция, а std::array — это библиотечная утилита, которая инкапсулирует его внизу. - person iammilind; 15.02.2020
comment
@SidS - стандарты C++ позволяют объявлять массивы в стиле C с размерами, определенными во время выполнения. int s=3; int myarr[s]{1,2,3}; отлично работает в C++ - person Plasty Grove; 15.02.2020
comment
@Blastfurnace - Вау, ты прав. Приведенный выше код работает с g++, но не работает с clang++, говоря: «объект переменного размера не может быть инициализирован». - person Plasty Grove; 15.02.2020
comment
@Blastfurnace - Вы правы, спасибо, что указали на это. Я думаю, что теперь это имеет смысл, и я понимаю, почему std::array должен иметь размер времени компиляции. - person Plasty Grove; 15.02.2020