Странно, что на Хабре до сих пор не было упомянуто о наделавшем шуму предложении к стандарту C++ под названием "Zero-overhead deterministic exceptions". Исправляю это досадное упущение. Если вас беспокоит оверхед исключений, или вам приходилось компилировать код без поддержки исключений, или просто интересно, что будет с обработкой ошибок в C++2b (отсылка к недавнему посту), прошу под кат. Вас ждёт выжимка из всего, что сейчас можно найти по теме, и пара опросов. Разговор далее будет вестись не только про статические исключения, но и про связанные предложения к стандарту, и про всякие другие способы обработки ошибок. Если вы зашли сюда поглядеть на синтаксис, то вот он: Если конкретный тип ошибки неважен/неизвестен, то можно использовать просто throws и catch (std::error e). Пусть мы решили, что ошибка, которая потенциально может возникнуть в функции, недостаточно «фатальная», чтобы бросать из неё исключение. Традиционно информацию об ошибке возвращают с помощью выходного параметра (out parameter). Например, Filesystem TS предлагает ряд подобных функций: (Не бросать же исключение из-за того, что файл не найден?) Тем не менее, обработка кодов ошибок громоздкая и подвержена багам. Код ошибки легко забыть проверить. Современные стили кода запрещают использование выходных параметров, вместо них рекомендуется возвращать структуру, содержащую весь результат. Boost вот уже некоторое время предлагает изящное решение для обработки таких «не-фатальных» ошибок, которые в определённых сценариях могут происходить сотнями в корректной программе: Тип expected похож на variant, но предоставляет удобный интерфейс для работы с «результатом» и «ошибкой». По умолчанию, в expected хранится «результат». Реализация file_size может выглядеть как-то так: Если причина ошибки нам неинтересна, или ошибка может заключаться только в «отсутствии» результата, то можно использовать optional: В C++17 из Boost в std попал optional (без поддержки optional<T&>); в C++20, возможно, добавят expected (это только Proposal, спасибо RamzesXI за поправку). Контракты (не путать с концептами) — новый способ наложить ограничения на параметры функции, добавленный в C++20. Добавлены 3 аннотации: Можно настроить, чтобы нарушение контрактов: Продолжать работу программы после нарушения контракта никак нельзя, потому что компиляторы используют гарантии из контрактов для оптимизации кода функции. Если есть малейшее сомнение в том, что контракт выполнится, стоит добавить дополнительную проверку. Библиотека <system_error>, добавленная в C++11, позволяет унифицировать обработку кодов ошибок в вашей программе. std::error_code состоит из кода ошибки типа int и указателя на объект какого-нибудь класса-наследника std::error_category. Этот объект, по сути, играет роль таблицы виртуальных функций и определяет поведение данного std::error_code. Чтобы создавать свои std::error_code, вы должны определить свой класс-наследник std::error_category и реализовать виртуальные методы, самым важным из которых является: Нужно также создать глобальную переменную вашего std::error_category. Обработка ошибок при помощи error_code + expected выглядит как-то так: Важно, что в std::error_code значение 0 означает отсутствие ошибки. Если для ваших кодов ошибок это не так, то перед тем, как конвертировать системный код ошибки в std::error_code, надо заменить код 0 на код SUCCESS, и наоборот. Все системные коды ошибок описаны в errc и system_category. Если на определённом этапе ручной проброс кодов ошибки становится слишком муторным, то всегда можно завернуть код ошибки в исключение std::system_error и выбросить. Пусть вам нужно создать очередной класс объектов, владеющих какими-нибудь ресурсами. Скорее всего, вы захотите сделать его некопируемым, но перемещаемым (moveable), потому что с unmoveable объектами неудобно работать (до C++17 их нельзя было вернуть из функции). Но вот беда: перемещённый объект в любом случае нужно удалить. Поэтому необходимо особое состояние "moved-from", то есть "пустого" объекта, который ничего не удаляет. Получается, каждый класс C++ обязан иметь пустое состояние, то есть невозможно создать класс с инвариантом (гарантией) корректности, от конструктора до деструктора. Например, невозможно создать корректный класс open_file файла, который открыт на всём протяжении времени жизни. Странно наблюдать это в одном из немногих языков, активно использующих RAII. Другая проблема — зануление старых объектов при перемещении добавляет оверхед: заполнение std::vector<std::unique_ptr<T>> может быть до 2 раз медленнее, чем std::vector<T*> из-за кучи занулений старых указателей при перемещении, с последующим удалением пустышек. Разработчики C++ давно облизываются на Rust, где у перемещённых объектов не вызываются деструкторы. Эта фича называется Destructive move. К сожалению, Proposal Trivially relocatable не предлагает добавить её в C++. Но проблему оверхеда решит. Класс считается Trivially relocatable, если две операции: перемещения и удаления старого объекта — эквивалентны memcpy из старого объекта в новый. Старый объект при этом не удаляется, авторы называют это "drop it on the floor". Тип является Trivially relocatable с точки зрения компилятора, если выполняется одно из следующих (рекурсивных) условий: Использовать эту информацию можно с помощью std::uninitialized_relocate, которая исполняет move init + delete обычным способом, или ускоренным, если это возможно. Предлагается пометить как  большинство типов стандартной библиотеки, включая std::string, std::vector, std::unique_ptr. Оверхед std::vector<std::unique_ptr<T>> с учётом этого Proposal исчезнет. Механизм исключений C++ разрабатывался в 1992 году. Были предложены различные варианты реализации. Из них в итоге был выбран механизм таблиц исключений, которые гарантируют отсутствие оверхеда для основного пути выполнения программы. Потому что с самого момента их создания создания предполагалось, что исключения должны выбрасываться очень редко.  Недостатки динамических (то есть обычных) исключений: Из-за этих недостатков существенно ограничивается область применения исключений. Когда исключения не могут применяться: По опросам, на местах работы 52% (!) разработчиков исключения запрещены корпоративными правилами. Но исключения — неотъемлемая часть C++! Включая флаг -fno-exceptions, разработчики теряют возможность использовать значительную часть стандартной библиотеки. Это дополнительно подстрекает компании насаждать собственные "стандартные библиотеки" и да, изобретать свой класс строки. Но и это ещё не конец. Исключения — единственный стандартный способ отменить создание объекта в конструкторе и выдать ошибку. Когда они отключены, появляется такая мерзость, как двухфазная инициализация. Операторы тоже не могут использовать коды ошибок, поэтому они заменяются функциями вроде assign. Герб Саттер (Herb Sutter) в P709 описал новый механизм передачи исключений. Идейно, функция возвращает std::expected, однако вместо отдельного дискриминатора типа bool, который вместе с выравниванием будет занимать до 8 байт на стеке, этот бит информации передаётся каким-то более быстрым способом, например, в Carry Flag. Функции, которые не трогают CF (таких большинство), получат возможность использовать статические исключения бесплатно — и в случае обычного возврата, и в случае проброса исключения! Функции, которые вынуждены будут его сохранять и восстанавливать, получат минимальный оверхед, и это всё равно будет быстрее, чем std::expected и любые обычные коды ошибок. Выглядят статические исключения следующим образом: В альтернативной версии предлагается обязать ставить ключевое слово try в том же выражении, что вызов throws функции: try i + safe_divide(j, k). Это сведёт число случаев использования throws функций в коде, не безопасном для исключений, практически к нулю. В любом случае, в отличие от динамических исключений, у IDE будет возможность как-то выделять выражения, бросающие исключения. То, что выброшенное исключение не сохраняется отдельно, а кладётся прямо на место возвращаемого значения, накладывает ограничения на тип исключения. Во-первых, он должен быть Trivially relocatable. Во-вторых, его размер должен быть не очень большим (но это может быть что-то вроде std::unique_ptr), иначе все функции будут резервировать больше места на стеке. Библиотека <system_error2>, разработанная Найл Дуглас (Niall Douglas), будет содержать status_code<T> — «новый, лучший» error_code. Основные отличия от error_code: Далее, вводится errored_status_code<T> — обёртка над status_code<T> со следующим конструктором: Тип исключения по умолчанию (throws без типа), а также базовый тип исключений, к которому приводятся все остальные (вроде std::exception) — это error. Он определён примерно так: То есть error — это такой «ошибочный» status_code, у которого значение (value) помещается в 1 указатель. Так как механизм status_code_category обеспечивает корректное удаление, перемещение и копирование, то теоретически в error можно сохранить любую структуру данных. На практике это будет один из следующих вариантов: Проблема в том, что случае 3 не планируется дать возможность привести error обратно к status_code<T>. Единственное, что можно сделать — получить message() упакованного status_code<T>. Чтобы иметь возможность достать обратно завёрнутое в error значение, надо выбросить его как динамическое исключение (!), потом поймать и завернуть в error. А вообще, Найл считает, что в error должны храниться только коды ошибок и строковые сообщения, чего достаточно для любой программы. Чтобы различать разные виды ошибок, предлагается использовать «виртуальный» оператор сравнения: Использовать несколько catch-блоков или dynamic_cast для выбора типа исключения не получится! Функция может иметь одну из следующих спецификаций: throws подразумевает noexcept. Если динамическое исключение выбрасывается из «статической» функции, то оно заворачивается в error. Если статическое исключение выбрасывается из «динамической» функции, то оно заворачивается в исключение status_error. Пример: Предложение предусматривает добавление исключений в один из будущих стандартов C, причём эти исключения будут ABI-совместимы со статическими исключениями C++. Структуру, аналогичную std::expected<T, U>, пользователь должен будет объявлять самостоятельно, хотя избыточность можно убрать с помощью макросов. Синтаксис состоит из (для простоты будем так считать) ключевых слов fails, failure, catch. При этом в C++ тоже можно будет вызывать fails функции из C, объявляя их в блоках extern C. Таким образом, в C++ будет целая плеяда ключевых слов по работе с исключениями: Итак, в C++ завезли (точнее, завезут) тележку новых инструментов для обработки ошибок. Далее возникает логичный вопрос: Ошибки разделяются на несколько уровней: В стандартной библиотеке надёжнее всего будет полностью отказаться от использования динамических исключений, чтобы сделать компиляцию «без исключений» легальной. Функции, использующие errno для быстрой и минималистичной работы с кодами ошибок C и C++, должны быть заменены на fails(int) и throws(std::errc), соответственно. Некоторое время старый и новый варианты функций стандартной библиотеки будут сосуществовать, потом старые объявят устаревшими. Ошибки выделения памяти обрабатывает глобальный хук new_handler, который может: Сейчас по умолчанию выбрасывается std::bad_alloc. Предлагается же по умолчанию вызывать std::terminate(). Если вам нужно старое поведение, замените обработчик на тот, который вам нужен, в начале main(). Все существующие функции стандартной библиотеки станут noexcept и будут крашить программу при std::bad_alloc. В то же время, будут добавлены новые API вроде vector::try_push_back, которые допускают ошибки выделения памяти. Исключения std::logic_error, std::domain_error, std::invalid_argument, std::length_error, std::out_of_range, std::future_error сообщают о нарушении предусловия функции. В новой модели ошибок вместо них должны использоваться контракты. Перечисленные типы исключений не будут объявлены устаревшими, но почти все случаи их использования в стандартной библиотеке будут заменены на . Proposal сейчас находится в состоянии черновика. Он уже довольно сильно поменялся, и ещё может сильно измениться. Некоторые наработки не успели опубликовать, так что предлагаемый API <system_error2> не совсем актуален. Предложение описывается в 3 документах: На настоящий момент не существует компилятора, который поддерживает статические исключения. Соответственно, сделать их бенчмарки пока невозможно. При наилучшем раскладе детерминированные исключения будут готовы и попадут в C++23. Если не успеют, то, вероятно, попадут в C++26, так как комитет стандартизации, в целом, заинтересован темой. Многие детали предлагаемого подхода к обработке исключений я опустил или умышленно упростил, зато прошёлся по большинству тем, требующихся для понимания статических исключений. Если возникли дополнительные вопросы, то задайте их в комментариях или обратитесь к документам по ссылкам выше. Любые поправки приветствуются. И конечно, обещанные опросы ^^