Создаются ли компиляторы для оптимизации исключения?

Мы обсуждали эту тему сегодня на работе, и никто из нас не мог дать окончательного ответа на этот вопрос. Рассмотрим следующую ситуацию:

int foo()
{
  int err;

  err = some_call(1);

  if (err != 0)
    return err;

  err = some_call(2);

  if (err != 0)
    return err;

  err = some_call(3);

  if (err != 0)
    return err;

  err = some_call(4);

  if (err != 0)
    return err;

  bar();

  return err;
}

Существует много повторений кода. Очевидно, что это может быть факторизовано с помощью макроса, к сожалению, не с шаблоном (из-за предложения return). Или, по крайней мере, не напрямую.

Теперь вопрос заключается в том, должны ли мы заменить те коды ошибок возврата с исключениями и сразу же перехватить эти исключения, являются ли компиляторы допустимыми и достаточно умными для обнаружения шаблона и вообще не исключают исключения?

Вот иллюстрация того, что я имею в виду:

int foo()
{
  try
  {
    // some_call now throws a ErrorReturned exception that contains the error code upon failure.
    some_call(1);
    some_call(2);
    some_call(3);
    some_call(4);
  }
  catch (ErrorReturned& ex)
  {
    return ex.error_code();
  }

  bar();

  return 0;
}

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

Короче, так ли это "хорошая" практика, и если да, могут ли компиляторы оптимизировать это, не исключая вообще исключения? (Предполагая, что конструкция исключения не имеет побочного эффекта)

Ответ 1

"Являются ли компиляторы достаточно умными", похоже, намекает, что Исключения не служат цели в вашем проекте, и, если это произойдет, вы не должны использовать их в первую очередь (если, конечно, у вас нет возможности получение исключения).

Краткий ответ: Нет, компиляторы не будут удалять ваши исключения/обработку исключений на основе шаблона, который вы используете.

Когда вы используете try/catch, исключение, которое он обрабатывает, добавляется в основную таблицу исключений; эту таблицу можно контролировать, подключать, добавлять и удалять. Просто потому, что вы поймаете исключение сразу же, это не значит, что с ним ничего не происходит.

Боковой источник:
Бумага была написана на Optimizing Away C++ Exception Handling, которая описывает все (почти все) текущие реализации оптимизаций, относящихся к исключениям. Во всем этом он показывает, что в настоящее время они не удаляются во время компиляции, но для них делается оптимизация. Сам документ рекомендует усовершенствования EH (обработка исключений) для удаления ненужных исключений и, в целом, довольно хорошее чтение.

ОБНОВЛЕНИЕ (дополнительные источники)
Глядя далее в эту тему, компилятор GCC, похоже, не оптимизирует исключения; однако он предлагает такую ​​возможность: -fno-exceptions. Эта опция удалит все исключения и сразу заменит их вызовами abort().

В другом источнике (fooobar.com/questions/320250/...) прямо не упоминается "удаление исключений", но описываются две оптимизации, сделанные для исключений, setjmp/longjmp и нулевая стоимость, Это можно сделать путем выделения фактических улучшений без упоминания "полного удаления исключения", что такой оптимизации нет (хотя, по крайней мере, с упомянутыми компиляторами). Другой источник с более подробной информацией об этих оптимизациях можно найти здесь.

Ответ 2

Повторение в вашем коде может быть тривиально удалено с помощью простого цикла:

int foo()
{
    for (int x : {1, 2, 3, 4})
    {
        int err = some_call(x);
        if (err != 0) return err;
    }
    bar();
    return 0;
}