Использование `throw;` в модифицированном исключении

У меня есть функция foo, которая может генерировать исключение bar.

В другой функции я вызываю foo, но у меня есть возможность добавить некоторые подробности в исключение bar, если оно было выбрано. (Я бы предпочел не передавать такую ​​информацию, как параметр, в foo, поскольку она действительно не существует из-за общего характера этой функции.)

Итак, я делаю это в вызывающем:

try {
    foo();
} catch (bar& ex){
    ex.addSomeMoreInformation(...);
    throw;
}

Будет ли throw перебросить измененное исключение или мне нужно использовать throw ex;? Последний предположительно возьмет ценную копию, поэтому я бы предпочел не делать этого. Может ли throw взять копию значения? Я подозреваю, что это не так.

(Я знаю, что могу проверить, но я заинтересован в том, чтобы наткнуться на неуказанную конструкцию или undefined, поэтому хотел бы знать наверняка).

Ответ 1

С++ 11 §15.1/8:

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

Ответ 2

Собственно, стандарт здесь очень точный. [Except.handle]/17:

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

И [except.throw]/8:

Выражение-выражение без операндов возвращает текущую обработку исключение (15.3).

Ответ 3

throw (без объекта исключения) восстановит текущее исключение. (должен быть внутри блока catch, иначе std:: terminate вызывается). Поскольку вы изменили ссылку на текущий объект исключения, вам не нужно явно пытаться выкинуть объект, а бросить повторное бросить измененное исключение и не создавать новый временный объект.

Ответ 4

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

Позвольте мне попытаться сделать разницу между этими явно выраженными примерами: -

class exception
{
};

class MyException : public exception
{
};

void func()
{
  try
  {
    throw MyException();
  }
  catch( exception& e )
  {
    //do some modification.
    throw;                    //Statement_1
    throw e;                  //Statement_2
   }
}

Statment_1: -

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

Statement_2: -

Это бросает "исключение", которое было первоначально обнаружено как "MyException", так как оно сделает копию снова. Итак, просто забудьте об изменениях, которые вы сделали, они даже не пройдут или исключают исключение для вызывающего. Он выдает "исключение" в процедуру вызова.

Надеюсь, что я понятен (и ПРАВО НА СЛЕДУЮЩИЙ СТАНДАРТ C++) достаточно...

Ответ 5

в соответствии с this, исключение исключений из С++ может быть выполнено двумя способами:

  • throw выражение: во-первых, copy-инициализирует объект исключения из выражения (это может вызвать конструктор перемещения для выражения rvalue, а копирование/перемещение может быть подвергнуто копированию), затем передает управление к обработчику исключений с типом соответствия, составной оператор которого или список инициализаторов членов был введен последним и не вышел из этого потока выполнения.
  • throw: переводит обработанное в настоящее время исключение. Отменяет выполнение текущего блока catch и передает управление следующему соответствующему обработчику исключений (но не к другому предложению catch после того же блока try: его составной оператор считается "выведенным" ), повторно используя существующий объект исключения: никаких новых объектов не производится. Эта форма разрешена только тогда, когда в настоящее время обрабатывается исключение (она вызывает std:: terminate, если используется иначе). Предложение catch, связанное с блоком-try-block, должно завершиться путем ретронирования, если оно используется в конструкторе.

Итак, чтобы подчеркнуть мой ответ, throw должен быть в порядке.