Для чего нужен throw без аргументов в C++?

Ответ

Выражение throw; без аргументов используется только внутри обработчика catch для повторной генерации (rethrow) текущего перехваченного исключения. Это позволяет частично обработать исключение (например, залогировать, освободить ресурс) и затем передать его дальше по стеку вызовов для полноценной обработки.

Ключевое отличие от throw e;:

try {
    throw std::runtime_error("Original error");
} catch (std::exception& e) {
    e = std::runtime_error("Modified"); // Модифицируем ссылку на объект исключения
    // throw e; // Бросит КОПИЮ объекта типа std::exception (срезка!), с сообщением "Modified"
    throw;     // Бросит ИСХОДНОЕ исключение типа std::runtime_error, с сообщением "Original error"
}

Практический пример (логирование с последующей передачей):

void riskyOperation() {
    try {
        // ... код, который может бросить различные исключения
        someLowLevelFunction();
    }
    catch (...) {
        // Логируем факт ошибки на этом уровне, но не знаем, как её обработать
        std::cerr << "[ERROR] Exception in riskyOperation() at " << __LINE__ << std::endl;
        // Освобождаем критический ресурс, если нужно
        releaseResource();
        // Передаём исключение наверх, сохраняя его оригинальный тип и состояние
        throw;
    }
}

Важно: throw; вне блока catch приводит к неопределенному поведению (вызову std::terminate).

Ответ 18+ 🔞

А, ну это классика, ёпта! Смотри, есть такая хитрая штука — throw; без всего. Её, сука, можно юзать только внутри блока catch, чтобы кинуть то же самое исключение дальше. Это как сказать: «Я тут, бля, ошибку увидел, может, даже в лог записал, но разбирайся с ней сам, я пас».

В чём, бля, разница с throw e;? Смотри, тут всё просто, но овердохуища важное:

try {
    throw std::runtime_error("Исходная ошибка");
} catch (std::exception& e) {
    e = std::runtime_error("Изменённая"); // Меняем объект по ссылке
    // throw e; // Кинёт КОПИЮ типа std::exception (срезка, бля!), с текстом "Изменённая"
    throw;     // А вот это кинёт ИСХОДНЫЙ std::runtime_error, с "Исходная ошибка"
}

Видишь подвох? Если через throw e; кидать, то оригинальный тип нахуй слетает, остаётся только базовый std::exception. А throw; — это как «отмена, я ничего не менял, вот вам ваш барахлящий объект обратно».

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

void опаснаяХерня() {
    try {
        // ... код, который может всё разъебать
        какаяТоФункция();
    }
    catch (...) {
        // Пишем в лог, что тут пизда
        std::cerr << "[ПИЗДЕЦ] Исключение в опаснойХерне() на строке " << __LINE__ << std::endl;
        // Чистим за собой, если что (закрываем файлы, соединения и т.д.)
        почиститьЗаСобой();
        // А дальше — не мои проблемы, пусть выше разбираются
        throw; // Кидаем ровно то, что поймали, без изменений
    }
}

И главное, запомни: если throw; написать вне блока catch — это прямой билет в std::terminate, программа просто накроется медным тазом. Так что не выёбывайся, используй только там, где поймал.