кроме спецификатора (начиная с C++11).
Noexcept-спецификация не является частью типа функции (так же , как динамические спецификации исключений ) и может появиться только как часть лямбды — описателя или верхнего уровень функции описателя при объявлении функции, переменных, не статические элементы данных типа функция, указатель на функцию, ссылка на функцию или указатель на функцию-член, а также при объявлении параметра или возвращаемого типа в одном из тех объявлений, которые в свою очередь оказываются указателем или ссылкой на функцию. Он не может появиться в объявлении typedef или псевдонима типа .
Спецификация noexcept является частью типа функции и может появляться как часть любого объявления функции .
Каждая функция на C++ non-throwing or potentially throwing :
- potentially-throwing functions are:
- функции, объявленные с непустой спецификацией динамического исключения
- функции, объявленные со спецификатором noexcept , выражение которых оценивается как false
- функции, объявленные без спецификатора noexcept , за исключением
- деструкторы, если деструктор любого потенциально построенного основания или члена не является potentially-throwing (see below)
- по умолчанию конструкторов , конструкторы копирования , перемещение конструкторов , которые неявно объявленные или дефолт по своей первой декларации, если только
- конструктор для базы или члена,который неявное определение конструктора будет вызывать следующим образом potentially-throwing (see below)
- подвыражение такой инициализации,например,выражение аргумента по умолчанию,имеет вид potentially-throwing (see below)
- инициализатором члена по умолчанию (только для конструктора по умолчанию)является potentially-throwing (see below)
- операторы сравнения , которые по умолчанию задаются в их первом объявлении, если только вызов какого-либо оператора сравнения в неявном определении не potentially-throwing (see below)
- deallocation functions
- не-бросающие функции — все остальные (те, у которых нет спецификатора noexcept, чье выражение оценивается как true , а также деструкторы, стандартные функции-члены по умолчанию и функции освобождения)
Явные экземпляры могут использовать спецификатор noexcept, но это не обязательно. Если используется, спецификация исключения должна быть такой же, как и для всех других объявлений. Диагностика требуется только в том случае, если спецификации исключений не совпадают в пределах одной единицы перевода.
Функции,отличающиеся только спецификацией исключения,не могут быть перегружены (так же,как и тип возврата,спецификация исключения является частью типа функции,но не частью сигнатуры функции)(начиная с C++17).
Указатели (включая указатели на функции-члены) на негенерирующие функции могут быть назначены или использованы для инициализации (до С++ 17) неявно преобразуются в (начиная с С++ 17) указатели на потенциально генерирующие функции, но не другие наоборот.
Если виртуальная функция не бросает,то все объявления,включая определение,каждого переопределителя также должны быть не бросающими,если только переопределитель не определен как удаленный:
Не бросающие функции могут вызывать потенциально бросающие функции. Всякий раз, когда генерируется исключение, и при поиске обработчика встречается самый внешний блок функции, не std::terminate вызывается функция std :: terminate :
Спецификация исключения спецификации шаблона функции не инстанцируется вместе с объявлением функции;она инстанцируется только тогда,когда needed (как определено ниже).
Исключение-спецификация неявно заявленной функции специального члена также оценивается только тогда,когда это необходимо (в частности,неявное объявление функции члена производного класса не требует инстанцирования исключения-спецификации функции базового члена).
Когда спецификация шаблона функции,кроме спецификации,является needed , но еще не было создано, ищутся зависимые имена, и создаются любые шаблоны, используемые в выражении, как будто для объявления специализации.
Считается,что функция,за исключением спецификации,имеет следующие характеристики needed в следующих контекстах:
- в выражении,где функция выбирается по разрешению перегрузки
- функция используется odr
- эта функция будет использоваться не по назначению,но появится в неоцененном операнде.
- спецификация необходима для сравнения с другим объявлением функции (например,о переопределении виртуальной функции или явной специализации шаблона функции).
- в определении функции
- спецификация нужна потому,что функция специального члена по умолчанию должна ее проверить,чтобы решить свою собственную спецификацию исключения (это происходит только тогда,когда спецификация функции специального члена по умолчанию сама по себе нужна).
Формальное определение potentially-throwing expression (используется для определения спецификации исключений по умолчанию для деструкторов, конструкторов и операторов присваивания, как описано выше):
Выражение e есть potentially-throwing if:
- e — это вызов функции, указатель на функцию или указатель на функцию-член, которая potentially-throwing , если e не является основным константным выражением (до C++17)
- e делает неявный вызов potentially-throwing функция (например, перегруженный оператор, функция распределения в new выражении, конструктор для аргумента функции или деструктор, если e является полным выражением)
- e это выражение — throw
- e — это dynamic_cast , приводящая к полиморфному ссылочному типу.
- e — это выражение typeid , применяемое к разыменованному указателю на полиморфный тип.
- e него есть непосредственное подвыражение, которое может быть
Notes
Одним из применений константного выражения (наряду с оператором noexcept ) является определение шаблонов функций, которые объявляют noexcept для некоторых типов, но не для других.
Обратите внимание, что спецификация noexcept для функции не является проверкой во время компиляции; это всего лишь метод для программиста, чтобы сообщить компилятору, должна ли функция генерировать исключения. Компилятор может использовать эту информацию для включения определенных оптимизаций функций, не noexcept , а также для включения оператора noexcept , который может проверять во время компиляции, объявлено ли определенное выражение для каких-либо исключений. Например, контейнеры, такие как std::vector будут перемещать свои элементы, если конструктор перемещения элементов равен noexcept , и копировать иначе (если конструктор копирования недоступен, но есть потенциально вызывающий конструктор перемещения, и в этом случае гарантируется сильное исключение). отменяется).
Deprecates
noexcept — это улучшенная версия throw() , которая устарела в C++11. throw() до C++17 , noexcept не будет вызывать std::unexpected , может или не может разматывать стек и будет вызывать std::terminate , что потенциально позволяет компилятору реализовать noexcept без накладных расходов времени выполнения на throw() . Начиная с C++17, throw() переопределяется, чтобы быть точным эквивалентом noexcept(true) .
Keywords
Example
Defect reports
Следующие отчеты о дефектах,изменяющих поведение,были применены ретроактивно к ранее опубликованным стандартам C++.
Спецификации исключений и спецификатор noexcept в С++
На этом уроке мы рассмотрим, что такое спецификации исключений в языке С++, а также использование спецификатора noexcept.
Спецификации исключений
В языке C++ все функции условно можно разделить на 2 типа:
функции, не выбрасывающие исключений;
функции, которые потенциально могут выбросить исключения.
Рассмотрим следующее объявление функции:
Глядя на типичное объявление функции, не представляется возможным определить, может ли функция выбросить исключение или нет. Несмотря на то, что комментарии могут помочь обозначить, выбрасывает ли функция исключения или нет (и если да, то какие именно исключения), у нас нет никакого специального компилятора для комментариев, а документация может устареть.
Спецификации исключений — это механизм языка C++, который изначально был разработан для документирования в пределах объявления функции того, какие исключения она может выбрасывать. Хотя большинство спецификаций исключений теперь устарели или удалены из стандарта, но в качестве замены была добавлена одна полезная спецификация исключений, которую мы рассмотрим на данном уроке.
Спецификатор noexcept
Спецификатор noexcept определяет функцию как не выбрасывающую исключений. Чтобы определить функцию как не выбрасывающую, мы можем использовать спецификатор noexcept в объявлении функции, поместив его справа от списка параметров функции:
Обратите внимание, что noexcept на самом деле не запрещает функции выбрасывать исключения или вызывать другие функции, которые потенциально могут выбросить исключения. Скорее всего, при возникновении исключения, если оно происходит из noexcept-функции, будет вызвана функция std::terminate(). И обратите внимание, что если std::terminate() вызывается внутри noexcept-функции, то раскручивание стека может происходить, а может и не происходить (в зависимости от реализации и оптимизации). А это означает, что ваши объекты могут быть уничтожены должным образом до завершения работы, а может и не произойти этого уничтожения.
Подобно тому, как функции, отличающиеся только своими возвращаемыми значениями, не могут быть перегружены, функции, отличающиеся только своей спецификацией исключений, также не могут быть перегружены.
Спецификатор noexcept с параметром типа bool
Спецификатор noexcept имеет необязательный параметр типа bool:
noexcept(true) равносильно noexcept, что означает, что функция не является выбрасывающей;
noexcept(false) означает, что функция относится к классу потенциально выбрасывающих исключения функций.
Эти параметры обычно используются только в шаблонных функциях, так что шаблонная функция может быть динамически создана как не выбрасывающая или потенциально выбрасывающая исключения на основе некоторого параметризованного значения.
Не выбрасывающие и потенциально выбрасывающие исключения функции
Функции, которые по умолчанию являются не выбрасывающими исключения:
Однако, если какая-либо из вышеперечисленных функций вызывает (явно или неявно) другую функцию, которая может выбросить исключение, то вызывающая функция также будет рассматриваться как потенциально выбрасывающая исключения. Например, если класс имеет элемент данных с потенциально выбрасывающим исключение конструктором, то конструкторы класса также будут рассматриваться как потенциально выбрасывающие исключения. В качестве другого примера, если оператор присваивания копированием вызывает потенциально выбрасывающий исключение оператор присваивания, то оператор присваивания копированием также будет рассматриваться как оператор, потенциально выбрасывающий исключения.
Совет: Если вы хотите, чтобы какая-либо из вышеперечисленных функций не выбрасывала исключений, то явно пометьте её как noexcept (даже если она является таковой по умолчанию), чтобы случайно данная функция не стала функцией, потенциально выбрасывающей исключение.
По умолчанию, потенциально выбрасывающими исключение функциями являются следующие объекты:
Оператор noexcept
Оператор noexcept может использоваться внутри функций. Он принимает в качестве аргумента выражение и возвращает true или false , если компилятор считает, что выражение не может или может выбросить исключение. Оператор noexcept проверяется статически во время компиляции и фактически не вычисляет входное выражение.
Оператор noexcept может использоваться для задания условия выполнения кода: является ли фрагмент кода потенциально выбрасывающим исключения или нет. Это необходимо для осуществления определенных гарантий безопасности исключений, о которых мы поговорим дальше.
Гарантии безопасности исключений
Гарантии безопасности исключений — это договоренности о том, как функции или классы будут вести себя в случае возникновения исключений. Существуют 4 уровня безопасности исключений:
Нет никаких гарантий — нет никаких гарантий относительно того, что произойдет, если возникнет исключение (например, класс может быть оставлен в непригодном для использования состоянии).
Базовая гарантия — если возникнет исключение, то утечки памяти не произойдет (все ресурсы будут освобождены корректно), и объект все еще будет использоваться, но программа может быть оставлена в измененном состоянии.
Строгая гарантия — если возникнет исключение, то утечки памяти не произойдет (все ресурсы будут освобождены корректно), состояние программы не будет изменено. Это означает, что функция должна корректно завершить свою работу, либо не иметь побочных эффектов в случае, если функция аварийно завершила свою работу. Простыми словами — если при выполнении операции возникнет исключение, то программа останется в том же состоянии, которое было до начала выполнения операции.
Гарантия отсутствия исключений/сбоев — работа функции всегда завершается успешно (без сбоев) или завершается аварийно, но без выбрасывания исключений.
Давайте рассмотрим пункт «Гарантия отсутствия исключений/сбоев» более подробно.
Гарантия отсутствия исключений: Если функция завершается аварийно, то она не будет выбрасывать исключение. Вместо этого она вернет код ошибки или проигнорирует проблему. Гарантии отсутствия исключений требуются во время раскручивания стека, когда исключение уже обрабатывается; например, все деструкторы должны иметь гарантию отсутствия исключения (как и любые функции, вызываемые этими деструкторами). Примеры кода, который относится к коду с отсутствием исключений:
деструкторы и функции освобождения/очистки памяти;
функции, которые вызывают другие функции, имеющие гарантии отсутствия исключений.
Гарантия отсутствия сбоев: Функция всегда успешно выполняет свою работу (и, следовательно, у нее никогда не будет необходимости выбрасывать исключения. Таким образом, гарантия отсутствия сбоя — это немного более сильная форма гарантии отсутствия исключения). Примеры кода, который относится к коду с отсутствием сбоев:
конструкторы перемещения и оператор присваивания перемещением;
контейнерные функции clear()/erase()/reset();
функции, которые должны вызывать другие функции, имеющие гарантии отсутствия сбоев.
Когда следует использовать спецификатор noexcept
Из того факта, что ваш код явно не выбрасывает никаких исключений, еще не следует, что вы должны начать указывать спецификатор noexcept на все подряд функции. По умолчанию, большинство функций потенциально способны выбросить исключение, поэтому, если ваша функция вызывает другие функции, есть вероятность того, что она вызывает функцию, которая потенциально способна выбросить исключение, и, следовательно, сама вызывающая функция будет относиться к потенциально выбрасывающим исключение функциям.
Принципы Стандартной библиотеки С++ состоят в том, чтобы использовать спецификатор noexcept только для тех функций, которые НЕ ДОЛЖНЫ выбрасывать исключения или аварийно завершать свою работу. Функции, которые потенциально способны выбросить исключения, но по факту не генерируют исключения (из-за реализации), как правило, не помечаются как noexcept-функции.
Советы:
Используйте спецификатор noexcept лишь в конкретных случаях, когда вы хотите явно указать на гарантию отсутствия сбоя или отсутствие выбрасывания исключения.
Если вы не уверены, должна ли функция иметь гарантию отсутствия сбоя/исключения, то лучше перестраховаться и не отмечать её с помощью noexcept. Решение использовать в таких случаях спецификатор noexcept нарушает обязательство взаимодействия с пользователем относительно поведения функции. Гораздо лучше потом при необходимости ужесточить гарантии безопасности, добавив спецификатор noexcept.
Почему полезно отмечать функции, как не выбрасывающие исключений функции
Есть пара веских причин, почему стоит помечать функции спецификатором noexcept:
Функции, которые являются noexcept, могут позволить компилятору выполнять некоторые оптимизации, которые в противном случае были бы недоступны. Поскольку
noexcept-функция не может выбрасывать исключения, то компилятору не нужно беспокоиться о сохранении стека времени выполнения в состоянии произвести раскручивание, что может позволить компилятору создавать более быстрый код.Есть также несколько случаев, когда знание, что функция относится к noexcept, позволяет нам создавать более эффективные реализации в нашем собственном коде: стандартные библиотечные контейнеры (такие как std::vector) знают о noexcept и будут использовать его для определения того, следует ли использовать семантику перемещения (быстрее) или семантику копирования (медленнее) в определенных местах.
Динамические спецификации исключений
До C++11 и даже до C++17, динамические спецификации исключений использовались вместо noexcept. Синтаксис динамических спецификаций исключений использует ключевое слово throw для перечисления типов исключений, которые функция может прямо или косвенно генерировать:
Name already in use
cpp-docs / docs / cpp / exception-specifications-throw-cpp.md
- Go to file T
- Go to line L
- Copy path
- Copy permalink
- Open with Desktop
- View raw
- Copy raw contents Copy raw contents
Copy raw contents
Copy raw contents
Exception specifications (throw, noexcept) (C++)
Exception specifications are a C++ language feature that indicate the programmer’s intent about the exception types that can be propagated by a function. You can specify that a function may or may not exit by an exception by using an exception specification. The compiler can use this information to optimize calls to the function, and to terminate the program if an unexpected exception escapes the function.
Prior to C++17 there were two kinds of exception specification. The noexcept specification was new in C++11. It specifies whether the set of potential exceptions that can escape the function is empty. The dynamic exception specification, or throw(optional_type_list) specification, was deprecated in C++11 and removed in C++17, except for throw() , which is an alias for noexcept(true) . This exception specification was designed to provide summary information about what exceptions can be thrown out of a function, but in practice it was found to be problematic. The one dynamic exception specification that did prove to be somewhat useful was the unconditional throw() specification. For example, the function declaration:
tells the compiler that the function does not throw any exceptions. However, in /std:c++14 mode this could lead to undefined behavior if the function does throw an exception. Therefore we recommend using the noexcept operator instead of the one above:
The following table summarizes the Microsoft C++ implementation of exception specifications:
Exception specification Meaning noexcept
noexcept(true)
throw()The function does not throw an exception. In /std:c++14 mode (which is the default), noexcept and noexcept(true) are equivalent. When an exception is thrown from a function that is declared noexcept or noexcept(true) , std::terminate is invoked. When an exception is thrown from a function declared as throw() in /std:c++14 mode, the result is undefined behavior. No specific function is invoked. This is a divergence from the C++14 standard, which required the compiler to invoke std::unexpected .
Visual Studio 2017 version 15.5 and later: In /std:c++17 mode , noexcept , noexcept(true) , and throw() are all equivalent. In /std:c++17 mode, throw() is an alias for noexcept(true) . In /std:c++17 mode and later, when an exception is thrown from a function declared with any of these specifications, std::terminate is invoked as required by the C++17 standard.noexcept(false)
throw(. )
No specificationThe function can throw an exception of any type. throw(type) (C++14 and earlier) The function can throw an exception of type type . The compiler accepts the syntax, but interprets it as noexcept(false) . In /std:c++17 mode and later, the compiler issues warning C5040. If exception handling is used in an application, there must be a function in the call stack that handles thrown exceptions before they exit the outer scope of a function marked noexcept , noexcept(true) , or throw() . If any functions called between the one that throws an exception and the one that handles the exception are specified as noexcept , noexcept(true) (or throw() in /std:c++17 mode), the program is terminated when the noexcept function propagates the exception.
The exception behavior of a function depends on the following factors:
Whether you are compiling the function under C or C++.
Which /EH compiler option you use.
Whether you explicitly specify the exception specification.
Explicit exception specifications are not allowed on C functions. A C function is assumed not to throw exceptions under /EHsc , and may throw structured exceptions under /EHs , /EHa , or /EHac .
The following table summarizes whether a C++ function may potentially throw under various compiler exception handling options:
20.9 – Спецификации исключений и noexcept
В C++ все функции классифицируются как не выбрасывающие исключения (не генерируют исключения) или потенциально выбрасывающие исключения (могут генерировать исключения).
Рассмотрим следующее объявление функции:
Глядя на типовое объявление функции, невозможно определить, может ли функция выбросить исключение или нет. Хотя комментарии могут помочь определить, генерирует ли функция исключения или нет (и если да, то какие исключения), документация может устареть, а компилятор не использует комментарии.
Спецификации исключений – это языковой механизм, который изначально был разработан как часть спецификации функций для документирования того, какие исключения может вызывать функция. Хотя большая часть спецификаций исключений теперь устарела или удалена, в качестве замены была добавлена другая полезная спецификация исключений, которую мы и рассмотрим в этом уроке.
Спецификатор noexcept
Спецификатор noexcept определяет функцию как не вызывающую исключения и используется в объявлении функции справа от списка параметров:
Обратите внимание, что noexcept на самом деле не мешает функции генерировать исключения или вызывать другие функции, которые потенциально могут генерировать исключения. Скорее, когда генерируется исключение, если оно выходит из функции noexcept , будет вызываться std::terminate . И обратите внимание, что если std::terminate вызывается из функции noexcept , раскручивание стека может произойти, а может и не произойти (в зависимости от реализации и оптимизации), что означает, что перед завершением программы ваши объекты могут быть, а могут и не быть разрушены корректным образом.
Как и функции, отличающиеся только своими возвращаемыми значениями, функции, отличающиеся только спецификацией исключения, так же не могут быть перегружены.
Спецификатор noexcept с логическим параметром
У спецификатора noexcept есть необязательный логический параметр. noexcept(true) эквивалентно noexcept , что означает, что функция не выбрасывает исключения. noexcept(false) означает, что функция потенциально может выбрасывать исключения. Эти параметры обычно используются только в шаблонах функций, где на основе некоторого параметризованного значения шаблонная функция может быть динамически создана как не генерирующая или как потенциально генерирующая исключения.
Какие функции не выбрасывают исключения, а какие потенциально выбрасывают исключения
Функции, которые по умолчанию не выбрасывают исключения:
- конструкторы по умолчанию;
- конструкторы копирования;
- конструкторы перемещения;
- деструкторы;
- операторы присваивания копированием;
- операторы присваивания перемещением.
Однако если какая-либо из перечисленных функций вызывает (явно или неявно) другую функцию, которая потенциально выбрасывает исключения, то указанная функция также будет рассматриваться как потенциально выбрасывающая исключения. Например, если в классе есть член данных с конструктором, потенциально выбрасывающим исключения, то конструкторы данного класса также будут рассматриваться как потенциально выбрасывающие исключения. В качестве другого примера, если оператор присваивания копированием вызывает оператор присваивания, потенциально выбрасывающий исключения, то этот оператор присваивания копированием также будет считаться бросать потенциально выбрасывающим исключения.
Лучшая практика
Если вы хотите, чтобы какие-либо из перечисленных выше функций были не выбрасывающими исключения, явно пометьте их как noexcept (даже если они заданы таким образом по умолчанию), чтобы они случайно не стали потенциально выбрасывающими исключения.
По умолчанию потенциально выбрасывающими исключения могут быть:
- обычные функции;
- пользовательские конструкторы;
- некоторые операторы, например, new .
Оператор noexcept
Оператор noexcept может использоваться внутри функций. Он принимает выражение в качестве аргумента и возвращает true или false , если компилятор считает, что аргумент вызовет исключение или нет. Оператор noexcept проверяется статически во время компиляции и на самом деле не вычисляет входное выражение.
Оператор noexcept может использоваться для условного выполнения кода в зависимости от того, является ли его аргумент потенциально выбрасывающим исключения или нет. Это необходимо для выполнения определенных гарантий безопасности исключений, о которых мы поговорим в следующем разделе.
Гарантии безопасности исключений
Гарантия безопасности исключений – это договоренность о том, как функции или классы будут вести себя в случае возникновения исключения. Существует четыре уровня безопасности исключений:
- Нет гарантии – нет никаких гарантий относительно того, что произойдет, если будет выброшено исключение (например, объект класса может остаться в непригодном для использования состоянии).
- Базовая гарантия – при возникновении исключения утечка памяти не происходит, и объект по-прежнему можно использовать, но программа может быть оставлена в измененном состоянии.
- Строгая гарантия – если возникнет исключение, утечка памяти не произойдет и состояние программы не изменится. Это означает, что функция должна либо полностью завершиться успешно, либо не иметь побочных эффектов в случае сбоя. Это может быть просто, если сбой произошел до того, как что-либо было изменено; но также это может быть достигнуто путем отката любых изменений, чтобы программа вернулась в состояние до сбоя.
- Гарантия отсутствия выбросов исключений / сбоев – функция всегда завершается успешно (без сбоя) или со сбоем, но без выброса исключения.
Давайте рассмотрим гарантию без выбросов исключений / без сбоев более подробно:
Гарантия отсутствия выбросов исключений: если функция дает сбой, она не вызывает исключения. Вместо этого она вернет код ошибки или проигнорирует проблему. Гарантии отсутствия выбросов исключений требуются во время раскручивания стека, когда исключение уже обрабатывается; например, все деструкторы должны иметь гарантию отсутствия выбросов исключений (как и любые функции, вызываемые этими деструкторами). Примеры кода, который должен быть не выбрасывающим исключения:
- деструкторы и функции освобождения/очистки памяти;
- функции, которые должны вызываться функциями более высокого уровня, не выбрасывающими исключения.
Гарантия отсутствия сбоев: функция всегда будет успешной в том, что она пытается сделать (и, таким образом, никогда не будет необходимости генерировать исключение, поэтому отсутствие сбоев – это немного более строгая форма отсутствия выбросов исключений). Примеры кода, который должен быть без сбоев:
- конструкторы перемещения и присваивание перемещением (семантика перемещения, описана в главе M);
- функции обмена (swap-функции);
- функции очистки/стирания/сброса контейнеров;
- операции с std::unique_ptr (также рассматриваются в главе M);
- функции, которые должны вызываться функциями более высокого уровня, не дающими сбоев.
Когда использовать noexcept
Тот факт, что ваш код не генерирует явно никаких исключений, не означает, что вы должны начать использовать noexcept везде, где только можно. По умолчанию большинство функций потенциально выбрасывают исключения, поэтому, если ваша функция вызывает другие функции, есть большая вероятность, что она вызовет функцию, которая потенциально выбрасывает исключения, и, следовательно, так же станет потенциально выбрасывающей исключения.
Политика стандартной библиотеки заключается в использовании noexcept только для функций, которые не должны выбрасывать исключения или давать сбой. Функции, которые потенциально могут генерировать исключения, но на самом деле не генерируют исключения (из-за реализации), обычно не помечаются как noexcept .
Лучшая практика
Используйте спецификатор noexcept в случаях, когда хотите показать гарантию отсутствия сбоев и выбросов исключений.
Лучшая практика
Если вы не уверены, должна ли функция иметь гарантию отсутствия сбоев / выбросов исключений, не помечайте ее как noexcept . Отмена решения об использовании noexcept нарушает обязательства интерфейса перед пользователем в отношении поведения функции. Повышение гарантий путем добавления noexcept позже считается безопасным.
Почему полезно помечать функции как не выбрасывающие исключения
Есть несколько веских причин отмечать функции как не выбрасывающие исключения:
- Функции, не выбрасывающие исключения, можно безопасно вызывать из функций, которые не безопасны для исключений, например, деструкторов.
- Функции с noexcept могут позволить компилятору выполнить некоторые оптимизации, которые в противном случае были бы недоступны. Поскольку функция noexcept не может генерировать исключение, компилятору не нужно беспокоиться о сохранении стека выполнения в нераскручиваемом состоянии, что может позволить ему создавать более быстрый код.
- Также есть несколько случаев, когда знание функции noexcept позволяют нам создавать более эффективные реализации в нашем собственном коде: контейнеры стандартной библиотеки (например, std::vector ) не выбрасывают исключения и будут использовать оператор noexcept , чтобы определить, использовать ли в некоторых местах семантику перемещения (быстрее) или семантику копирования (медленнее) (семантику перемещения мы рассмотрим в главе M).
Динамические спецификации исключений
Дополнительные материалы
До C++11 и до C++17 вместо noexcept использовались динамические спецификации исключений. Синтаксис динамических спецификаций исключений использует ключевое слово throw для перечисления типов исключений, которые функция может прямо или косвенно генерировать:
Из-за таких факторов, как неполные реализации компиляторов, некоторая несовместимость с шаблонами функций, распространенное недопонимание того, как они работают, и тот факт, что стандартная библиотека в основном их не использовала, динамические спецификации исключений были объявлены устаревшими в C++11 и удалены из языка в C++17 и C++20. Для получения более подробной информации смотрите этот документ.