Вопрос

Я разрабатываю иерархию исключений на C++ для своей библиотеки.«Иерархия» — это 4 класса, производные от std::runtime_error.Я хотел бы избежать проблема с нарезкой для классов исключений таким образом были защищены конструкторы копирования.Но, очевидно, gcc требует вызова конструктора копирования при создании их экземпляров, поэтому жалуется на конструкторы защищенного копирования.Visual C++ 8.0 прекрасно компилирует тот же код.Есть ли какой-нибудь переносимый способ решить проблему нарезки для классов исключений?Говорит ли стандарт что-нибудь о том, может ли реализация требовать конструктор копирования класса, который должен быть создан?

Это было полезно?

Решение

Я бы воздержался от разработки иерархии исключений, отличной от вашей библиотеки.Использовать std::exception иерархия, насколько это возможно, и всегда извлекайте свои исключения из чего-то внутри этой иерархии.Возможно, вы захотите прочитать часть исключений из часто задаваемых вопросов по C++ Маршалла Клайна - читать Часто задаваемые вопросы 17.6, 17.9, 17.10, и 17.12 в частности.

Что касается "заставлять пользователей ловить по ссылке", я не знаю хорошего способа сделать это.Единственный способ, который я придумал примерно за час игры (сейчас воскресенье, полдень), основан на полиморфное метание:

class foo_exception {
public:
    explicit foo_exception(std::string msg_): m_msg(msg_) {}
    virtual ~foo_exception() {}
    virtual void raise() { throw *this; }
    virtual std::string const& msg() const { return m_msg; }
protected:
    foo_exception(foo_exception const& other): m_msg(other.m_msg) {}
private:
    std::string m_msg;
};

class bar_exception: public foo_exception {
public:
    explicit bar_exception(std::string msg_):
        foo_exception(msg_), m_error_number(errno) {}
    virtual void raise() { throw *this; }
    int error_number() const { return m_error_number; }
protected:
    bar_exception(bar_exception const& other):
        foo_exception(other), m_error_number(other.m_error_number) {}
private:
    int m_error_number;
};

Идея состоит в том, чтобы сделать конструктор копирования защищенным и заставить пользователей вызывать Class(args).raise() вместо throw Class(args).Это позволяет вам генерировать полиморфно связанное исключение, которое ваши пользователи могут перехватить только по ссылке.Любая попытка поймать по значению должен будет встречено хорошим предупреждением компилятора.Что-то вроде:

фу.cpp:59:ошибка:«bar_Exception::bar_Exception(const bar_Exception&)» защищен

foo.cpp:103:ошибка:в этом контексте

Конечно, за все это приходится платить, поскольку вы больше не можете использовать throw явно, иначе вы получите аналогичное предупреждение компилятора:

фу.cpp:В функции «void h()»:

фу.cpp:31:ошибка:«foo_Exception::foo_Exception(const foo_Exception&)» защищено

фу.cpp:93:ошибка:в этом контексте

фу.cpp:31:ошибка:«foo_Exception::foo_Exception(const foo_Exception&)» защищено

фу.cpp:93:ошибка:в этом контексте

В целом, я бы полагался на стандарты кодирования и документацию, в которых указано, что вы всегда должны улавливать по ссылке.Убедитесь, что ваша библиотека перехватывает исключения, которые она обрабатывает по ссылке, и генерирует новые объекты (например, throw Class(constructorArgs) или throw;).Я ожидаю, что другие программисты C++ будут обладать такими же знаниями, но на всякий случай добавьте примечание в любую документацию.

Другие советы

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

Решение вашей проблемы состоит в том, чтобы вместо этого всегда ловить по ссылке:

try {
    // some code...
    throw MyException("lp0 is on fire!");
} catch (MyException const &ex) {
    // handle exception
}

(const-ness является необязательным, но я всегда включаю его, потому что редко возникает необходимость изменять объект исключения.)

Ответ Томаса правильный, но я также хотел бы предложить вам не тратить время на «разработку иерархии исключений».Проектирование иерархий классов — крайне плохая идея, особенно если вы можете просто вывести пару (и не более того) новых типов исключений из стандартных классов исключений C++.

Два портативных способа, которые я нашел, чтобы помешать клиентам моей библиотеки неправильно перехватывать исключения по значению:

  1. Выбрасывать исключения изнутри виртуальное повышение методы классов исключений и обеспечить защиту конструкторов копирования.(Спасибо Д.Шоли)
  2. Выбрасывайте производные исключения из библиотеки и публикуйте базовые классы исключений, чтобы клиенты могли их перехватывать.Базовые классы могли бы иметь защищенные конструкторы копирования, что обеспечивает только хороший способ их перехвата.(упомянул здесь по аналогичному вопросу)

Стандарт C++ утверждает, что конструктор копирования должен быть доступен в момент вызова.Visual C++ 8.0 в моей конфигурации нарушил эту часть стандарта, не обеспечивая наличие конструктора копирования.В разделе 15.1.3:

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

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

Этот ответ был опубликован ОП в вопросе, я удалил его из вопроса и опубликовал как отдельный ответ.

Я бы посоветовал не использовать какой-либо встроенный код исключений C++.Если вам необходимы исключения, создайте свои собственные с нуля.Это единственный способ быть уверенным, что они будут вести себя одинаково, не говоря уже о том, чтобы быть реализованными одинаковым образом, и, говоря прямо, реализация исключений в C++ некомпетентна.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top