Реализация конструктора копирования для exception
Вглядываясь в доки к std::exception
, обнаруживается, что конструктор копирования исключения (std::exception(const std::exception&)
), декларирован, как noexcept
. Более того, стандарт требует, чтобы каждое исключение в стандарте декларировало конструктор копирования как noexcept
. Не понимаю, как это вообще возможно реализовать, ведь исключение по идеи должно хранить внутри себя что-то вроде std::string
, чтобы сохранять сообщение от пользователя, когда кидается исключение, и, вследствии этого, копировать эту самую строку при копировании исключения. Но в таком случае может быть нарушено обещание noexcept
, когда конструктор std::string
кидает std::bad_alloc
.
Подскажите, пожалуйста, как реализовать класс исключения так, чтобы конструктор копирования исключения не нарушал обещание noexcept
Ответы (1 шт):
Причиной требования noexcept
является потенциальная возможность исключительного кошмара исключений при обработке исключений. С этой точки зрения, желательно вообще ничего не копировать, что и олицетворяет реализация std::exception
, в которой копируется только указатель на таблицу виртуальных функций, а поясняющая строка задаётся при определении класса исключения.
Если пояснительная строка должна создаваться динамически, как в иерархиях исключений std::logic_error
и std::runtime_error
, то, ввиду динамического создания пояснительной строки, принято идти на компромисс и допускать возможность исключений в конструкторе. Под капотом стандартных реализаций обычно используются: "строки с подсчётом ссылок", "строки Copy-On-Write" и т.п.
Если Вам такой компромисс подходит, то логично не изобретать велосипед, а использовать наследника от std::logic_error
в качестве корня вашей иерархии исключений.
В случае же, сравнительной жесткости ваших требований к надёжности и/или эффективности, но при необходимости задавать поясняющую строку литералом по месту, то в качестве корня вашей иерархии возможен компромисс типа:
class my_exception_base : public std::exception {
const char *_msg;
public:
template <size_t N>
explicit my_exception_base(const char (&s)[N]) noexcept {
_msg = s;
}
char const * what() const noexcept {
return _msg;
}
};
Строго говоря, этот класс слишком простой и, хотя шаблон блокирует основные потенциальные ошибки, его можно использовать некорректно. К примеру, извернуться и передать в него char x[] = "bad msg";
со стека (при изощрённом метапрограммировании C++, в принципе, можно блокировать всё, кроме литералов, но...).