Рассмотрим следующий фрагмент:
#include <array>
int main() {
using huge_type = std::array<char, 20*1024*1024>;
huge_type t;
}
Очевидно, что это приведет к сбою на большинстве платформ, поскольку размер стека по умолчанию обычно составляет менее 20 МБ.
Теперь рассмотрим следующий код:
#include <array>
#include <vector>
int main() {
using huge_type = std::array<char, 20*1024*1024>;
std::vector<huge_type> v(1);
}
Удивительно, но и вылетает! Трассировка (с одной из последних версий libstdc ++) приводит к include/bits/stl_uninitialized.h
файлу, где мы можем видеть следующие строки:
typedef typename iterator_traits<_ForwardIterator>::value_type _ValueType;
std::fill(__first, __last, _ValueType());
Конструктор изменения размера vector
должен инициализировать элементы по умолчанию, и вот как это реализовано. Очевидно, _ValueType()
временный сбой стека.
Вопрос в том, соответствует ли это реализации. Если да, то это на самом деле означает, что использование вектора огромных типов довольно ограничено, не так ли?
std::allocator
.Ответы:
Нет ограничений на то, сколько автоматического хранилища использует любой стандартный API.
Всем им может потребоваться 12 терабайт стекового пространства.
Однако этот API требует только
Cpp17DefaultInsertable
, и ваша реализация создает дополнительный экземпляр над тем, что требуется конструктору. Если это не скрыто за обнаружением объекта, который легко можно копировать и копировать, эта реализация выглядит незаконной.источник
std::allocator
. Я не уверен, почему этот особый случай сделан в первую очередь.std::fill
для тривиальных типов, которые затем используют,memcpy
чтобы разбить байты на места, что потенциально намного быстрее, чем создание множества отдельных объектов в цикле. Я считаю, что реализация libstdc ++ соответствует, но причиной переполнения стека для больших объектов является ошибка качества реализации (QoI). Я сообщил об этом как gcc.gnu.org/PR94540 и исправлю это.Я оспариваю предположение о «большинстве». Поскольку память огромного объекта никогда не используется, компилятор может полностью ее игнорировать и никогда не выделять память, в этом случае не произойдет сбой.
Стандарт C ++ не ограничивает использование стека и даже не подтверждает существование стека. Итак, да, это соответствует стандарту. Но можно считать это вопросом качества реализации.
Это похоже на случай с libstdc ++. Сбой не был воспроизведен с помощью libc ++ (с использованием clang), поэтому кажется, что это не ограничение в языке, а скорее только в этой конкретной реализации.
источник
Я не адвокат по языкам и не эксперт по стандарту C ++, но cppreference.com говорит:
Возможно, я неправильно понимаю «вставлено по умолчанию», но я ожидаю:
быть эквивалентным
Последняя версия не должна создавать стековую копию, а должна создавать тип large_type непосредственно в динамической памяти вектора.
Я не могу достоверно сказать, что то, что вы видите, не соответствует требованиям, но это, конечно, не то, что я ожидал бы от качественной реализации.
источник
std::allocator
, поэтому не должно быть заметной разницы между вставкой непосредственно в память векторов и созданием промежуточной копии.emplace_back
но не для создания вектора. Это означает, что вы можете иметь,vector<mutex> v(1)
но неvector<mutex> v; v.emplace_back();
для чего-то подобного, уhuge_type
вас все еще может быть операция выделения и перемещения со второй версией. Никто не должен создавать временные объекты.vector::vector(size_type, Allocator const&)
требует (Cpp17) DefaultInsertable