Обработка исключений и приведение

try
{
throw Derived();
}
catch (Base&)
{
std::cout << "subtyping\n";
}

try
{
throw "lol";
}
catch (std::string)
{
std::cout << "coercion\n";
}

Выход:

subtyping
terminate called after throwing an instance of 'char const*'

Почему обработка исключений хорошо работает с подтипами, а не с принуждением?

12

Решение

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

3 основных отличия:

  • исключения всегда копируются хотя бы один раз (их вообще невозможно избежать)
  • catch пункты рассматриваются в порядке их объявления (не подходят)
  • они подвержены меньшему количеству форм преобразования типов:
    • наследственные покрытия,
    • преобразование из типизированного в нетипизированный указатель (const void* ловит любой указатель)

Любой другой вид конверсии не допускается (например, int в doubleили неявный const char* в string — твой пример).

По поводу вашего вопроса в комментарии
Предположим, существует иерархия:

class Base {};
class Derived: public Base {};
class Base2 {};
class Leaf: public Derived, public Base2 {};

Теперь в зависимости от порядка catch пункты, соответствующий блок будет выполнен.

try {
cout << "Trying ..." << endl;
throw Leaf();

} catch (Base& b) {
cout << "In Base&";

} catch (Base2& m) {
cout << "In Base2&"; //unreachable due to Base&

} catch (Derived& d) {
cout << "In Derived&";  // unreachable due to Base& and Base2&
}

Если вы переключаетесь Base а также Base2 поймать порядок вы заметите другое поведение.
Если Leaf унаследовано в частном порядке от Base2, затем catch Base2& будет недоступен вне зависимости от того, где находится (при условии, что мы бросаем Leaf)

Вообще все просто: порядок имеет значение.

18

Другие решения

Пункт 15.3 / 3 Стандарта C ++ 11 определяет точные условия для соответствия обработчика определенному объекту исключения, и они не позволяют пользовательские преобразования:

Обработчик соответствует объекту исключения типа E если

— Обработчик типа cv T или же cv T& а также E а также T одного типа (игнорируя верхний уровень cv-qualifiers),
или же

— обработчик типа cv T или же cv T& а также T является однозначным публичным базовым классом E, или же

— обработчик типа cv1 T* cv2 а также E тип указателя, который может быть преобразован в тип
обработчик одним или обоими

  • стандартное преобразование указателя (4.10), не включающее преобразование указателей в приватные или защищенные
    или неоднозначные классы

  • преобразование квалификации

— обработчик является указателем или указателем на тип элемента и E является std::nullptr_t,

[…]
8