Навіщо мені std :: get_temporary_buffer?


84

З якою метою я повинен використовувати 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його назва?


Один із варіантів використання було запропоновано в журналі "Доктор Добб" за 1 липня 1996 р. Для реалізації алгоритмів:

Якщо жоден буфер не може бути виділений, або якщо він менший, ніж запитується, алгоритм все одно працює коректно, він просто гальмує.


2
FYI, std::get_temporary_bufferбуде припинено в C ++ 17.
Декінг

1
@Deqing Так. Він також буде видалений у C ++ 20 і з поважної причини (як зазначені нижче). Тож рухайтеся вздовж глядача ..
Нікос,

Відповіді:


44

Страуструп говорить у "Мові програмування на C ++" ( §19.4.4 , SE):

Ідея полягає в тому, що система може тримати ряд буферів фіксованого розміру готовими до швидкого розподілу, так що запит місця для n об'єктів може дати простір більше n . Однак це може принести і менше, тому один із способів використання get_temporary_buffer()- оптимістично попросити багато, а потім використовувати те, що трапляється, доступне.
[...] Оскільки get_temporary_buffer()низькорівневий і, ймовірно, буде оптимізований для управління тимчасовими буферами, його не слід використовувати як альтернативу new або allocator :: allocate () для отримання довгострокового зберігання.

Він також починає вступ до двох функцій з:

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

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

У анекдоті в "Від математики до загального програмування" згадується, що Степанов надав фіктивну реалізацію заповнювача в оригінальному дизайні STL:

На його подив, він виявив роками пізніше, що всі основні постачальники, що надають реалізації STL, все ще використовують цю жахливу реалізацію [...]


12
Схоже, реалізація VC ++ цього просто викликає цикл operator newіз послідовно меншими аргументами, поки розподіл не вдасться. Особливих оптимізацій там немає.
jalf

9
Те саме з g ++ 4.5 - схоже, він був задуманим, але проігнорований продавцями.
Georg Fritzsche

4
crazy_allocator
Здається,

Але будьмо серйозними - можливо, хтось із задоволенням виділить багато місця для зберігання. Зараз ми можемо попросити систему отримати таку величезну пам’ять - використовуючи get_temporary_buffer. Однак, якщо ми отримуємо менше, ніж запитувана сума (що було б справді шкода), ми продовжуємо намагатися робити свою роботу з наявним сховищем. Може бути краще, ніж ловити bad_allocвинятки, спричинені спробою виділити більше пам'яті, ніж доступно. Однак справжня корисність залишається і падає при хорошій реалізації.
0xbadf00d

@jalf Це застаріло в C ++ 17 :) (згідно cppreference.com ).
4LegsDrivenCat

17

Хлопець зі стандартної бібліотеки Microsoft каже наступне ( тут ):

  • Не могли б ви пояснити, коли використовувати 'get_temporary_buffer'

Він має дуже спеціалізоване призначення. Зверніть увагу, що він не створює винятків, таких як new (nothrow), але також не створює об'єкти, на відміну від new (nothrow).

Він використовується внутрішньо STL в таких алгоритмах, як stable_partition (). Це трапляється, коли є чарівні слова, такі як N3126 25.3.13 [alg.partitions] / 11: stable_partition () має складність "Найбільше (останній - перший) * журнал (останній - перший) свопи, але лише лінійна кількість свопів, якщо є достатньо додаткової пам'яті ". Коли з'являються магічні слова "якщо вистачає додаткової пам'яті", STL використовує get_temporary_buffer () для спроби отримати робочий простір. Якщо це можливо, то він може реалізовувати алгоритм ефективніше. Якщо це неможливо, оскільки система працює небезпечно близько до місця, де не вистачає пам'яті (або діапазони, що беруть участь, величезні), алгоритм може повернутися до більш повільної техніки.

99,9% користувачів STL ніколи не повинні знати про get_temporary_buffer ().


9

Стандарт стверджує, що він виділяє пам’ять до кількох n елементів. Іншими словами, ваш приклад може повернути буфер, достатній лише для 5 об’єктів.

Здається, досить важко уявити хороший варіант використання для цього. Можливо, якщо ви працюєте на дуже обмеженій пам'яті платформі, це зручний спосіб отримати "якомога більше пам'яті".

Але на такій обмеженій платформі, я гадаю, ти обійшов би розподілювач пам’яті якомога більше і використовував би пул пам’яті або щось, що ти маєш над повним контролем.


4

З якою метою я повинен використовувати std::get_temporary_buffer?

Функція застаріла в C ++ 17, тож правильна відповідь тепер "ні для чого, не використовуйте її".


2
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 ) );

фактичний розмір основи повинен бути більшим або рівним потребі .


2

Можливо (лише здогадка) це пов’язано із фрагментацією пам’яті. Якщо ви в значній мірі продовжуєте розподіляти та вивільняти тимчасову пам'ять, але кожного разу, коли ви це робите, ви виділяєте деяку довгострокову пам'ять після розподілу темпу, але перед його делокацією, у вас може вийти фрагментована купа (я думаю).

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


1
Дуже цікава думка. Хоча в даний час це, здається, реалізовується як дозволяє-просто-зробити-щось-що-працює в більшості реалізацій, це цілком може бути більш щільно захищене та інтегроване з іншими процедурами управління пам'яттю. Я голосую за нас, насправді очікуючи, що це буде для цього придатним, і коментарі Бьярнеса, схоже, теж натякають на це.
gustaf r

Зараз я шукав, що про це говорить Бьярн, і він каже, що він призначений для швидкого розподілу без ініціалізації. Отже, це було б як оператор void * лише для розподільника (не ініціалізатор) new (size_t size), але це швидше розподілити, оскільки він попередньо виділений.
Даніель Муньос
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.