Як зазначали інші, assert
це свого роду останній захист від помилок програміста, які ніколи не повинні відбуватися. Вони перевіряють обгрунтованість, які, мабуть, сподіваємось, не підлягають лівій і правій час, коли ви відправляєте.
Він також розроблений таким чином, щоб його не можна було отримати зі стійких версій версій з будь-яких причин, які розробники можуть вважати корисними: естетику, продуктивність, що б вони хотіли. Це частина того, що відокремлює збірку налагодження від збірки випуску, і за визначенням збірка випуску позбавлена таких тверджень. Таким чином, є підрив дизайну, якщо ви хочете випустити аналогічну "збірку випуску з твердженнями на місці", яка була б спробою складання випуску з _DEBUG
визначенням препроцесора і не NDEBUG
визначена; це насправді вже не збірка версій.
Дизайн поширюється навіть на стандартну бібліотеку. Як дуже базовий приклад серед численних, багато реалізацій std::vector::operator[]
буде assert
перевірка здоровості, щоб переконатися, що ви не перевіряєте вектор поза межами. І стандартна бібліотека почне працювати набагато, набагато гірше, якщо включити такі перевірки у версії версії. Орієнтир vector
використанняoperator[]
і ctor заповнення з такими твердженнями, що входять до простого старого динамічного масиву, часто показуватиме динамічний масив значно швидшим, поки ви не відключите такі перевірки, тому вони часто роблять ефективність впливу далеко не тривіальними способами. Перевірка нульового вказівника тут і перевірка поза межами дійсно можуть стати величезними витратами, якщо такі перевірки застосовуються мільйони разів у кожному кадрі в критичних петлях, що передують коду, просто, як перенаправлення розумного покажчика або доступ до масиву.
Таким чином, ви, швидше за все, бажаєте іншого інструменту для роботи та такого, який не призначений для того, щоб його не було опущено з версій версій, якщо ви хочете складати версії, які виконують такі перевірки надійності у ключових областях. Найкорисніше, що я особисто знаходжу - це лісозаготівля. У тому випадку, коли користувач повідомляє про помилку, речі стають набагато простішими, якщо він додає журнал, і останній рядок журналу дає мені велику підказку щодо того, де сталася помилка та що це може бути. Тоді, відтворюючи їхні кроки у налагодженні, я можу також отримати збій у твердженні, і ця недостатність твердження надає мені величезні підказки для впорядкування мого часу. Але оскільки реєстрація є відносно дорогою, я не використовую її для застосування надзвичайно низького рівня перевірок рівня безпеки, наприклад, щоб переконатися, що масив не має доступу поза межами загальної структури даних.
Але нарешті, і дещо погоджуючись з вами, я міг побачити розумний випадок, коли ви, можливо, хочете передати тестерам щось, що нагадує збірку налагодження під час альфа-тестування, наприклад, з невеликою групою альфа-тестерів, які, скажімо, підписали NDA . Там це може впорядкувати альфа-тестування, якщо ви передасте тестерам щось інше, ніж збірка повного випуску, яка додає інформацію про налагодження разом із деякими функціями налагодження / розробки, такими як тести, які вони можуть запускати, та більш багатослівний вихід під час роботи програмного забезпечення. Я принаймні бачив, як великі ігрові компанії роблять такі речі для альфа. Але це щось на зразок альфа чи внутрішнього тестування, коли ви справді намагаєтесь надати тестерам щось інше, ніж збірку версій. Якщо ви насправді намагаєтеся поставити збірку релізів, то, за визначенням, цього не повинно бути_DEBUG
визначено, інакше це дійсно заплутує різницю між збіркою "налагодження" та "випуск".
Чому цей код потрібно видалити перед випуском? Перевірки не є великими збитками від продуктивності, і якщо вони не спрацьовують, то, безумовно, є проблема, про яку я віддаю перевагу більш прямому повідомленню про помилку.
Як було зазначено вище, перевірки не обов'язково тривіальні з точки зору ефективності. Багато хто, ймовірно, тривіальні, але знову ж таки, навіть стандартна версія використовує їх, і це може вплинути на ефективність неприйнятними способами для багатьох людей у багатьох випадках, якщо, скажімо, обхід випадковим доступом std::vector
триває 4 рази довше в тому, що повинно бути оптимізованою версією версії через його межі, які перевіряють, що ніколи не повинно провалюватися.
У колишній команді нам насправді довелося змусити нашу бібліотеку матриць і векторів виключити деякі твердження в певних критичних шляхах, щоб змусити роботу налагодження працювати швидше, тому що ці твердження сповільнювали математичні операції на порядок більше до тієї точки, де вона була починаючи вимагати від нас зачекати 15 хвилин, перш ніж ми зможемо навіть простежити код, що цікавить. Мої колеги насправді хотіли просто знятиasserts
відверто, тому що вони виявили, що саме це зробило колосальну зміну. Натомість ми зупинилися на тому, щоб зробити так, щоб критичні шляхи налагодження уникали їх. Коли ми змусили ці критичні шляхи використовувати дані вектор / матриця безпосередньо, не проходячи перевірку меж, час, необхідний для виконання повної операції (яка включала більше, ніж просто векторна / матрична математика), скорочувався з хвилин на секунду. Це надзвичайний випадок, але, безумовно, твердження не завжди є незначними з точки зору ефективності, навіть не зблизька.
Але також це просто так asserts
, як вони розроблені. Якщо вони не мали б такого величезного впливу на продуктивність у всьому платі, то я можу прихилити це, якби вони були розроблені як більш, ніж функція збирання налагодження, або ми могли б використовувати, vector::at
що включає межі перевірки навіть у версії версій та викидах за межі доступ, наприклад (але з величезним хітом продуктивності). Але в даний час я вважаю їх дизайн набагато кориснішим, враховуючи їхній величезний вплив на продуктивність у моїх випадках, як функцію, що працює лише налагодження, яка опускається, коли NDEBUG
визначено. Що стосується випадків, з якими я працював, принаймні, це має величезну різницю для складання випуску, щоб виключити перевірки правильності, які насправді ніколи не повинні бути збитими.
vector::at
vs. vector::operator[]
Я думаю, що відмінність цих двох методів лежить в основі цього, як і альтернативи: винятки. vector::operator[]
реалізацій, як правило, assert
щоб переконатися, що вихід поза межі викликає помилку, що легко відтворюється, коли намагається отримати доступ до вектора поза межами. Але реалізатори бібліотеки роблять це з припущенням, що це не буде коштувати ні копійки в оптимізованій збірці випусків.
Тим часом vector::at
передбачено, що завжди перевіряє поза межами меж і кидає навіть у складі релізів, але за це виконує покарання за ефективність до того моменту, коли я часто бачу набагато більше коду, vector::operator[]
ніж використання vector::at
. Багато дизайну C ++ перегукується з думкою "платити за те, що ти використовуєш / потребуєш", і багато людей часто віддають перевагу operator[]
, що навіть не турбується про те, що межі перевіряють версії версій, виходячи з того, що вони роблять не потрібно перевіряти межі в їх оптимізованих версіях випусків. Якщо раптом у версії версій увімкнено твердження, продуктивність цих двох буде однаковою, а використання вектора завжди буде повільніше, ніж динамічний масив. Отже, величезна частина дизайну та користі тверджень базується на ідеї, що вони стають вільними у складанні релізів.
release_assert
Це цікаво після виявлення цих намірів. Звичайно, всі випадки використання були б різними, але, я думаю, я знайшов би певне використання, для release_assert
якого робиться перевірка і зірве програмне забезпечення, що показує номер рядка та повідомлення про помилку навіть у версії версій.
Це було б для деяких незрозумілих випадків у моєму випадку, коли я не хочу, щоб програмне забезпечення витончено відновилося так, як це було б, якщо викинутий виняток. Я б хотів, щоб він вийшов з ладу навіть у випуску у тих випадках, щоб користувачеві можна було вказати номер рядка, щоб повідомити про те, що програмне забезпечення натрапило на те, що ніколи не повинно трапитися, все ще в царині перевірок правильності наявності помилок програміста, а не зовнішніх помилок введення, наприклад винятки, але досить дешеві, щоб зробити їх, не турбуючись про його вартість у випуску.
Насправді є деякі випадки, коли я можу виявити важкий збій із номером рядка та повідомленням про помилку, бажано витончено відновитись після викинутого винятку, що може бути досить дешевим, щоб зберегти у випуску. І є деякі випадки, коли неможливо відновити виняток, як-от помилка, яка виникає при спробі відновлення з існуючого. Там я знайшов би ідеальне пристосування для, release_assert(!"This should never, ever happen! The software failed to fail!");
і, природно, це буде брудним дешевим, оскільки перевірка буде виконуватися в першу чергу на винятковому шляху і не коштуватиме нічого при звичайних шляхах виконання.