Зачем мне std :: get_ Contemporary_buffer?

С какой целью мне следует использовать std::get_temporary_buffer? Стандарт гласит следующее:

Получает указатель на хранилище, достаточное для хранения до n смежных T объектов.

Я думал, что буфер будет размещен в стеке, но это не так. Согласно стандарту C ++ этот буфер на самом деле не временный. Какие преимущества у этой функции перед глобальной функцией ::operator new, которая тоже не создает объекты. Правильно ли я, что следующие утверждения эквивалентны?

int* x;
x = std::get_temporary_buffer<int>( 10 ).first;
x = static_cast<int*>( ::operator new( 10*sizeof(int) ) );

Эта функция существует только для синтаксического сахара? Почему в его названии есть temporary?


Один вариант использования был предложен в Dr. Dobb's Journal, 1 июля 1996 г. для реализации алгоритмов:

Если буфер не может быть выделен или он меньше запрошенного, алгоритм по-прежнему работает правильно, он просто замедляется.


person Kirill V. Lyadvinsky    schedule 16.07.2010    source источник
comment
К вашему сведению, std::get_temporary_buffer будет устаревшим в C ++ 17.   -  person Deqing    schedule 26.07.2016
comment
@ Требуется Да. Он также будет удален в C ++ 20 и по уважительной причине (как указано ниже). Так что двигайтесь по зрителю ..   -  person KeyC0de    schedule 29.10.2018


Ответы (6)


Страуструп говорит в «Язык программирования C ++» (§19.4.4, SE):

Идея состоит в том, что система может держать несколько буферов фиксированного размера готовыми к быстрому распределению, так что запрос пространства для n объектов может дать пространство для более чем n. Однако он также может давать меньше, поэтому один из способов использования get_temporary_buffer() - это оптимистично попросить много, а затем использовать то, что оказывается доступным.
[...] Потому что get_temporary_buffer() является низкоуровневым и, вероятно, будет оптимизирован для управляя временными буферами, его не следует использовать в качестве альтернативы new или allocator :: allocate () для получения более длительного хранения.

Он также начинает знакомство с двумя функциями с:

Алгоритмам часто требуется временное пространство для нормальной работы.

... но, похоже, нигде не дает определения временного или долгосрочного.

анекдот в " От математики к общему программированию " упоминается, что Степанов предоставил фиктивную реализацию заполнителя в исходном проекте STL, однако :

К своему удивлению, годы спустя он обнаружил, что все основные поставщики, предлагающие реализации STL, все еще используют эту ужасную реализацию [...]

person Georg Fritzsche    schedule 16.07.2010
comment
Похоже, что реализация этого в VC ++ представляет собой просто цикл, вызывающий operator new с последовательно уменьшающимися аргументами, пока распределение не будет успешным. Никаких особых оптимизаций нет. - person jalf; 16.07.2010
comment
То же самое с g ++ 4.5 - кажется, это было сделано из лучших побуждений, но продавцы проигнорировали его. - person Georg Fritzsche; 20.07.2010
comment
Похоже, они должны были обернуть эту функциональность в класс под названием crazy_allocator - person 0xbadf00d; 21.06.2014
comment
Но давайте будем серьезными - может быть, кто-то будет счастлив выделить много места для хранения. Теперь мы можем попросить систему получить такой огромный объем памяти - используя get_temporary_buffer. Однако, если мы получаем меньше запрошенной суммы (что было бы очень жаль), мы продолжаем пытаться работать с имеющимся у нас хранилищем. Может быть лучше, чем ловить bad_alloc исключения, вызванные попыткой выделить больше памяти, чем доступно. Однако настоящая полезность остается и падает с хорошей реализацией. - person 0xbadf00d; 21.06.2014
comment
@jalf В C ++ 17 он устарел :) (согласно cppreference.com). - person 4LegsDrivenCat; 26.08.2016

Специалист по стандартной библиотеке Microsoft говорит следующее (здесь):

  • Не могли бы вы объяснить, когда использовать get_ Contemporary_buffer?

У него очень специализированное предназначение. Обратите внимание, что он не генерирует исключений, таких как new (nothrow), но также не создает объекты, в отличие от new (nothrow).

Он используется внутри STL в таких алгоритмах, как stable_partition (). Это происходит, когда есть волшебные слова вроде N3126 25.3.13 [alg.partitions] / 11: stable_partition () имеет сложность «Максимум (последний - первый) * журнал (последний - первый) свопов, но только линейное количество свопов, если есть достаточно дополнительной памяти ". Когда появляются волшебные слова «если достаточно дополнительной памяти», STL использует get_porary_buffer (), чтобы попытаться получить рабочее пространство. Если да, то алгоритм может быть реализован более эффективно. Если это невозможно, потому что система работает в опасной близости от нехватки памяти (или задействованные диапазоны огромны), алгоритм может вернуться к более медленной технике.

99,9% пользователей STL никогда не узнают о get_ Contemporary_buffer ().

person Jeremy    schedule 11.11.2014

В стандарте говорится, что он выделяет память для до n элементов. Другими словами, ваш пример может вернуть буфер, достаточно большой только для 5 объектов.

Тем не менее, довольно сложно представить себе хороший вариант использования для этого. Возможно, если вы работаете на платформе с очень ограниченным объемом памяти, это удобный способ получить «как можно больше памяти».

Но на такой ограниченной платформе, я полагаю, вы обойдете в максимально возможной степени распределитель памяти и используете пул памяти или что-то, что вы полностью контролируете.

person jalf    schedule 16.07.2010

С какой целью я должен использовать std::get_temporary_buffer?

Функция устарела в C ++ 17, поэтому правильный ответ теперь " ни с какой целью, не используйте его ".

person Raedwald    schedule 06.12.2017

ptrdiff_t            request = 12
pair<int*,ptrdiff_t> p       = get_temporary_buffer<int>(request);
int*                 base    = p.first;
ptrdiff_t            respond = p.sencond;
assert( is_valid( base, base + respond ) );

ответ может быть меньше, чем запрос.

size_t require = 12;
int*   base    = static_cast<int*>( ::operator new( require*sizeof(int) ) );
assert( is_valid( base, base + require ) );

фактический размер base должен быть больше или равен require.

person OwnWaterloo    schedule 16.07.2010

Возможно (только предположение) это как-то связано с фрагментацией памяти. Если вы продолжаете интенсивно выделять и освобождать временную память, но каждый раз, когда вы это делаете, вы выделяете некоторую долгосрочную предполагаемую память после выделения временной памяти, но до ее освобождения, вы можете получить фрагментированную кучу (я думаю).

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

person Daniel Munoz    schedule 06.01.2012
comment
Очень интересная мысль. Хотя в настоящее время он, кажется, реализован как давайте-просто-сделай-что-то-работает в большинстве реализаций, он вполне мог бы получить более жесткую поддержку и интегрировать с остальными подпрограммами управления памятью. Я голосую за нас, на самом деле ожидая, что это подходит для этого, и комментарии Бьярнеса, кажется, тоже на это намекают. - person gustaf r; 08.01.2013
comment
Я посмотрел, что говорит об этом Бьярн, и он говорит, что он разработан для быстрого выделения без инициализации. Таким образом, это было бы похоже на void * operator new (size_t size) только для распределителя (не инициализатор), но это быстрее выделяется, поскольку оно предварительно выделено. - person Daniel Munoz; 01.02.2013