Подтвердить что ты не робот

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

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*'

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

4b9b3361

Ответ 1

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

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)

Как правило, это просто: порядок.

Ответ 2

Параграф 15.3/3 стандарта С++ 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.

[...]