Между типом возбужденного исключения и типом исключения, указанного в спецификации, не разрешается проводить никаких преобразований:
int convert( int parm ) throw(string)
{
//...
if ( somethingRather )
// ошибка программы:
// convert() не допускает исключения типа const char*
throw "help!";
}
Выражение throw в функции convert() возбуждает исключение типа строки символов в стиле языка C. Созданный объект-исключение имеет тип const char*. Обычно выражение типа const char* можно привести к типу string. Однако спецификация не допускает преобразования типов, поэтому если convert() возбуждает такое исключение, то вызывается unexpected(). Для исправления ошибки выражение throw можно модифицировать так, чтобы оно явно преобразовывало значение выражения в тип string:
throw string( "help!" );
11.4.1. Спецификации исключений и указатели на функции
Спецификацию исключений можно задавать и при объявлении указателя на функцию.
Например:
void (*pf)( int ) throw(string);
В этом объявлении говорится, что pf указывает на функцию, которая способна возбуждать только исключения типа string. Как и для объявлений функций, спецификации исключений в разных объявлениях одного и того же указателя не суммируются, они должны быть одинаковыми:
extern void (*pf) ( int ) throw(string);
// ошибка: отсутствует спецификация исключения
void (*pf)( int );
При работе с указателем на функцию со спецификацией исключений есть ограничения на тип указателя, используемого в качестве инициализатора или стоящего в правой части присваивания. Спецификации исключений обоих указателей не обязаны быть идентичными. Однако на указатель-инициализатор она должна накладывать столь же или более строгие ограничения, чем на инициализируемый указатель (или тот, которому присваивается значение). Например:
void recoup( int, int ) throw(exceptionType);
void no_problem() throw();
void doit( int, int ) throw(string, exceptionType);
// правильно: ограничения, накладываемые на спецификации
// исключений recoup() и pf1, одинаковы
void (*pf1)( int, int ) throw(exceptionType) =
// правильно: ограничения, накладываемые на спецификацию исключений no_problem(),
более строгие,
// чем для pf2
void (*pf2)( ) throw(string) = &no_problem;
// ошибка: ограничения, накладываемые на спецификацию
// исключений doit(), менее строгие, чем для pf3
//
void (*pf3)( int, int ) throw(string) =
Третья инициализация не имеет смысла. Объявление указателя гарантирует, что pf3 адресует функцию, которая может возбуждать только исключения типа string. Но doit() возбуждает также исключения типа exceptionType. Поскольку она не подходит под ограничения, накладываемые спецификацией исключений pf3, то не может служить корректным инициализатором для pf3, так что компилятор выдает ошибку.
Упражнение 11.9
В коде, разработанном для упражнения 11.8, измените объявление оператора operator[]() в классе IntArray, добавив спецификацию возбуждаемых им исключений. Модифицируйте программу так, чтобы operator[]() возбуждал исключение, не указанное в спецификации. Что при этом происходит?
Упражнение 11.10
Какие исключения может возбуждать функция, если ее спецификация исключений имеет вид throw()? А если у нее нет такой спецификации?
Упражнение 11.11
Какое из следующих присваиваний ошибочно? Почему?
void example() throw(string);
(a) void (*pf1)() = example;
(b) void (*pf2) throw() = example;
11.5. Исключения и вопросы проектирования
С обработкой исключений в программах C++ связано несколько вопросов. Хотя поддержка такой обработки встроена в язык, не стоит использовать ее везде. Обычно она применяется для обмена информацией об ошибках между независимо разработанными частями программы. Например, автор некоторой библиотеки может с помощью исключений сообщать пользователям об ошибках. Если библиотечная функция обнаруживает аномальную ситуацию, которую не способна обработать самостоятельно, она может возбудить исключение для уведомления вызывающей программы.