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

В чем разница между броском и броском с аргументом исключенного?

Представьте себе два похожих фрагмента кода:

try {
  [...]
} catch (myErr &err) {
  err.append("More info added to error...");
  throw err;
}

и

try {
  [...]
} catch (myErr &err) {
  err.append("More info added to error...");
  throw;
}

Являются ли они такими же или отличаются ли они каким-то тонким способом? Например, первый из них вызывает запуск конструктора копий, тогда как, возможно, второй повторяет один и тот же объект для его ретронирования?

4b9b3361

Ответ 1

В зависимости от того, как вы упорядочили свою иерархию исключений, повторное бросание исключения путем присвоения имени переменной исключения в инструкции throw может срезать исходный объект исключения.

Выражение выражения без аргумента будет вызывать текущий объект исключения, сохраняющий его динамический тип, тогда как выражение throw с аргументом генерирует новое исключение, основанное на статическом типе аргумента, на throw.

например.

int main()
{
    try
    {
        try
        {
            throw Derived();
        }
        catch (Base& b)
        {
            std::cout << "Caught a reference to base\n";
            b.print(std::cout);
            throw b;
        }
    }
    catch (Base& b)
    {
        std::cout << "Caught a reference to base\n";
        b.print(std::cout);
    }

    return 0;
}

Как указано выше, программа выведет:

Caught a reference to base
Derived
Caught a reference to base
Base

Если throw b заменить на throw, то внешний catch также поймает изначально выброшенное исключение Derived. Это все еще выполняется, если внутренний класс ловит исключение Base по значению вместо ссылки - хотя это, естественно, означает, что исходный объект исключения не может быть изменен, поэтому любые изменения в b не будут отображаться в Derived исключение, пойманное внешним блоком.

Ответ 2

Во втором случае в соответствии с конструктором копирования С++ Standard 15.1/6 не используется:

Выражение-выражение без операнда перепроводит обрабатываемое исключение. Исключение возобновляется с существующим временным; не создается новый объект временного исключения. Исключение больше не считается пойманным; поэтому значение uncaught_exception() снова будет истинным.

В первом случае новое исключение будет выбрано в соответствии с 15.1/3:

Выражение-выражение инициализирует временный объект, называемый объектом исключения, тип которого определяется удалением всех cv-квалификаторов верхнего уровня из статического типа операнда броска и настройки тип из "массива T" или "функция, возвращающая T", на "указатель на T" или "указатель на функцию возврата T", соответственно. <... > Временное значение используется для инициализации переменной, указанной в соответствующем обработчике (15.3). Тип выражения-броска не должен быть неполный тип или указатель или ссылка на неполный тип, кроме void *, const void *, volatile void * или const volatile void *. За исключением этих ограничений и ограничений на типа, упомянутого в 15.3, операнд throw обрабатывается точно как аргумент функции в вызове (5.2.2) или операндом оператора return.

В обоих случаях конструктор копирования требуется на этапе броска (15.1/5):

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