Спецификации исключений при выводе из std::exception в C++11

У меня есть класс исключения следующим образом:

#include <exception>

struct InvalidPathException : public std::exception
{
   explicit InvalidPathException() {}
   const char* what() const;
};

const char*
InvalidPathException::what() const {
    return "Path is not valid";
}

При компиляции в GCC 4.4 с -Wall -std= C++0x

ошибка: более слабый спецификатор броска для 'виртуального константного символа * InvalidPathException::what() const'

ошибка: переопределение 'виртуального const char* std::exception::what() const throw ()'

Совершенно верно, так как я переопределяю std::exception"s what() метод, который действительно имеет throw() спецификатор исключения. Но, как часто сообщают, мы не должны использовать спецификаторы исключений. И, насколько я понимаю, они устарели в C++ 11, но, очевидно, еще не в GCC с -std = C++0x.

Так что я был бы заинтересован в наилучшем подходе на данный момент. В коде, который я разрабатываю, я беспокоюсь о производительности и поэтому беспокоюсь о часто упоминаемых издержках throw(), но на самом деле это накладные расходы настолько серьезны? Правильно ли я думаю, что я буду страдать только тогда, когда what() фактически вызывается, что было бы только после того, как было сгенерировано такое исключение (и аналогично для других методов, унаследованных от std:: exception, которые все имеют throw() спецификаторы)?

В качестве альтернативы, есть ли способ обойти эту ошибку, данную GCC?

1 ответ

Решение

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

И из-за возможностей оптимизации, nothrow-спецификации не устарели в следующем стандарте, они просто не похожи throw () больше, но называются noexcept, Ну да, и они работают немного по-другому.

Вот обсуждение noexcept это также подробно объясняет, почему традиционные спецификации nothrow оптимизируют работу сайта вызываемого абонента.

Как правило, вы платите за каждый throw у вас есть спецификация, по крайней мере, с полностью совместимым компилятором, который GCC, по-видимому, не всегда был. Те throw спецификации должны быть проверены во время выполнения, даже пустые. Это потому, что если возникает исключение, которое не соответствует throw спецификация, раскрутка стека должна происходить в пределах этого стекового фрейма (так что вам нужен код для этого в дополнение к проверке соответствия), а затем std::unexpected должен быть вызван. С другой стороны, вы потенциально экономите время / пространство для каждого пустого throw спецификация как компилятор может сделать больше предположений при вызове этой функции. Я выдохну, сказав, что только профилировщик может дать вам окончательный ответ о том, страдает или улучшается ваш конкретный код (пусто!) throws Спецификация.

В качестве обходного пути к вашей актуальной проблеме, могут ли работать следующие?

  • Вводить #define NOTHROW throw () и использовать его для вашего исключения what и другие вещи.
  • Когда GCC реализует noexcept, переопределить NOTHROW,

Обновить

Как отмечает @James McNellis, throw () будет вперед-совместимым. В этом случае я предлагаю просто использовать throw () где вы должны и, кроме этого, если есть сомнения, профиль.

Другие вопросы по тегам