Подтвердить что ты не робот

Является ли исключение ссылкой ссылкой опасным?

Обратите внимание на следующее исключение: "

void some_function() {
    throw std::exception("some error message");
}

int main(int argc, char **argv) {
    try {
        some_function();
    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
        exit(1);
    }
    return 0;
}

Безопасно ли поймать исключение броска по ссылке?

Меня беспокоит то, что исключение e на самом деле помещено в стек some_function(). Но some_function() только что вернулся, в результате чего e будет разрушено. Итак, теперь e указывает на разрушенный объект.

Является ли мое беспокойство правильным?

Каков правильный способ передачи исключения без копирования его по значению? Должен ли я бросать new std::exception(), чтобы он помещался в динамическую память?

4b9b3361

Ответ 1

Это действительно безопасно - и рекомендуется - улавливать ссылку const.

"e фактически помещается в стек some_function()"

Нет, это не так... фактически созданный объект создается в неопределенной области памяти, зарезервированной для использования механизмом обработки исключений:

[except.throw] 15.1/4: Память для объекта исключений выделяется неопределенным способом, за исключением случаев, указанных в 3.7.4.1. Исключение объект уничтожается после того, как либо последний оставшийся активный обработчик для исключения выходит любыми способами, кроме реверсирования, или уничтожается последний объект типа std:: exception_ptr (18.8.5), который ссылается на объект исключения, в зависимости от того, что наступит позже.

Если локальная переменная указана на throw, она копируется там - если необходимо (оптимизатор может иметь возможность напрямую создавать ее в этой другой памяти). Вот почему...

15.1/5 Когда брошенный объект является объектом класса, конструктор, выбранный для инициализации копирования и деструктора должны быть доступны, даже если операция копирования/перемещения завершена (12.8).


Если это не щелкнуть, это может помочь представить реализацию смутно следующим образом:

// implementation support variable...
thread__local alignas(alignof(std::max_align_t))
    char __exception_object[EXCEPTION_OBJECT_BUFFER_SIZE];

void some_function() {
    // throw std::exception("some error message");

    // IMPLEMENTATION PSEUDO-CODE:
    auto&& thrown = std::exception("some error message");
    // copy-initialise __exception_object...
    new (&__exception_object) decltype(thrown){ thrown };
    throw __type_of(thrown);
    // as stack unwinds, _type_of value in register or another
    // thread_local var...
}

int main(int argc, char **argv)
{
    try {
        some_function();
    } // IMPLEMENTATION:
      // if thrown __type_of for std::exception or derived...
      catch (const std::exception& e) {
        // IMPLEMENTATION:
        // e references *(std::exception*)(&__exception_object[0]);
        ...
    }
}

Ответ 2

Вы должны поймать по ссылке, иначе вы не могли бы получить правильный динамический тип объекта. Что касается его срока службы, стандарт гарантирует, что в [except.throw],

Объект исключений уничтожается после того, как либо последний оставшийся активный обработчик для исключения выходит любыми способами, кроме реверсирования, или уничтожается последний объект типа std:: exception_ptr (18.8.5), который ссылается на объект исключения, в зависимости от того, что наступит позже

Ответ 3

Захват с помощью ссылки const - это именно то, как следует исключать исключения. Объект исключения не обязательно должен находиться в стеке. Компилятор отвечает за соответствующую магию, чтобы сделать эту работу.

С другой стороны, ваш пример не может скомпилироваться, поскольку std::exception может быть сконфигурирован по умолчанию или создан для копирования. В этом случае метод what() возвращает указатель на пустую строку (c-style), что не особенно полезно.

Предложите, чтобы вы выбрали std::runtime_error или std::logic_error, если это необходимо, или полученный из него класс:

  • logic_error, когда вызывающий абонент запросил что-то вне параметров дизайна вашей службы.
  • runtime_error, когда вызывающий абонент запросил что-то разумное, но внешние факторы мешают вам выполнить запрос.

http://en.cppreference.com/w/cpp/error/exception

Ответ 4

Из except.throw:

Бросание копии исключения инициализирует (8.5, 12.8) временный объект, называемый объектом исключения. Временной является значением lvalue и используется для инициализировать переменную, объявленную в соответствующем обработчике (15.3). Если тип объекта исключения будет неполным или указатель на неполный тип, отличный от (возможно, cv-qualified) void программа плохо сформирована.

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