void f() noexcept(noexcept(g())); // f() имеет тот же спецификатор

                                  // исключения, что и g()

Если функция g() обещает не передавать исключений, то f() также не будет. Если g() не имеет спецификатора исключения или имеет спецификатор, позволяющий передачу исключений, то функция f() также может передавать их.

Ключевое слово noexcept имеет два значения: это спецификатор исключения, когда оно следует за списком параметров функции, и оператор, который зачастую используется как логический аргумент для спецификатора исключения noexcept.

Спецификации исключения и указатели, виртуальные функции, функции управления копированием

Хотя спецификатор noexcept не является частью типа функции, наличие у функции спецификатора исключения влияет на ее использование.

Указатель на функцию и функция, на которую указывает этот указатель, должны иметь одинаковые спецификации. Таким образом, если объявлен указатель со спецификатором запрета передачи исключения, то использовать этот указатель можно только для указания на функции с подобным спецификатором. Указатель на функцию, способную передавать исключение, определенный явно или неявно, может указывать на любую функцию, даже если она обещает не передавать исключения:

// recoup() и pf1() обещают не передавать исключений

void (*pf1)(int) noexcept = recoup;

// ok: recoup() не будет передавать исключений; и не имеет значения,

// что pf2() может

void (*pf2)(int) = recoup;

pf1 = alloc; // ошибка: alloc() может передать исключение, но pf1()

             // обещала, что не будет

pf2 = alloc; // ok: pf2() и alloc() могли бы передать исключение

Если виртуальная функция обещает не передавать исключений, унаследованные виртуальные функции также должны обещать не передавать исключений. С другой стороны, если базовая функция позволяет передачу исключения, то производным функциям стоит быть ограниченным строже и обещать не передавать их:

class Base {

public:

 virtual double f1(double) noexcept; // не передает исключения

 virtual int f2() noexcept(false);   // может передавать

 virtual void f3();                  // может передавать

};

class Derived : public Base {

public:

 double f1(double);         // ошибка: Base::f1() обещает не передавать

 int f2() noexcept (false); // ok: та же спецификация, как у Base::f2()

 void f3() noexcept;        // ok: Derived:f3() ограничена строже

};

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

Упражнения раздела 18.1.4

Упражнение 18.8. Пересмотрите написанные классы и добавьте соответствующие спецификации исключения к их конструкторам и деструкторам. Если вы полагаете, что некоторые из ваших деструкторов могли бы передавать исключения, изменить код так, чтобы это было невозможно.

<p>18.1.5. Иерархии классов исключений</p>

Классы исключений (см. раздел 5.6.3) стандартной библиотеки формируют иерархию наследования (см. главу 15), представленную на рис. 18.1.

Рис. 18.1. Иерархия классов исключений стандартной библиотеки

Единственными функциями, определенными типом exception, являются конструктор копий, оператор присвоения копий, виртуальный деструктор и виртуальная функция-член what(). Она возвращает указатель типа const char* на символьный массив с нулевым символом в конце и, как гарантируется, не передает никаких исключений.

Перейти на страницу:

Похожие книги