C++
Domain_error в C++: как спровоцировать и зачем это нужно
std::domain_error — это стандартное исключение C++, которое используют, когда аргумент функции выходит за допустимую область значений. Например, функция ожидает положительное число, а получила отрицательное
Самый прямой способ спровоцировать domain_error — бросить его вручную через throw
#include <stdexcept>
throw std::domain_error("negative value is not allowed");
Но в нормальном коде исключение бросают не просто ради ошибки, а в месте проверки входных данных
Пример с квадратным корнем
Математически квадратный корень из отрицательного числа в вещественных числах не определен. Поэтому можно написать безопасную функцию:
#include <cmath>
#include <iostream>
#include <stdexcept>
double checkedSqrt(double x) {
if (x < 0) {
throw std::domain_error("sqrt argument must be non-negative");
}
return std::sqrt(x);
}
int main() {
try {
std::cout << checkedSqrt(-4) << "\n";
} catch (const std::domain_error& error) {
std::cout << "Ошибка: " << error.what() << "\n";
}
return 0;
}
Вывод будет похож на:
Ошибка: sqrt argument must be non-negative
Такой пример хорошо показывает смысл domain_error: аргумент не подходит для области определения функции
Почему std::sqrt не всегда бросает domain_error
Важно: стандартная std::sqrt обычно не бросает std::domain_error при отрицательном аргументе. В зависимости от реализации и настроек она может вернуть NaN, установить errno или поднять floating-point exception
Поэтому если вам нужно именно исключение std::domain_error, надежнее сделать проверку самим:
if (x < 0) {
throw std::domain_error("x must be >= 0");
}
Это особенно удобно в учебных задачах и в коде, где вы хотите явно отлавливать неверные аргументы
Пример с процентом
Допустим, функция принимает процент скидки от 0 до 100:
#include <iostream>
#include <stdexcept>
double applyDiscount(double price, double percent) {
if (percent < 0 || percent > 100) {
throw std::domain_error("discount percent must be from 0 to 100");
}
return price * (1.0 - percent / 100.0);
}
int main() {
try {
std::cout << applyDiscount(1000, 150) << "\n";
} catch (const std::domain_error& error) {
std::cout << error.what() << "\n";
}
return 0;
}
Здесь 150 не подходит по смыслу, поэтому domain_error уместен
Когда использовать domain_error
Используйте std::domain_error, когда значение имеет неправильную область допустимых значений:
- отрицательный аргумент там, где допустимы только положительные числа;
- процент вне диапазона 0-100;
- номер месяца меньше 1 или больше 12;
- радиус окружности меньше 0.
Если ошибка связана не с областью значений, а с форматом, файлом, памятью или индексом, может подойти другое исключение
Как использовать в проверочном тесте
Спровоцировать domain_error часто нужно не в основной программе, а в тесте: вы хотите убедиться, что функция правильно реагирует на плохой аргумент
Без тестового фреймворка это можно проверить так:
bool thrown = false;
try {
checkedSqrt(-1);
} catch (const std::domain_error&) {
thrown = true;
}
if (!thrown) {
std::cout << "Тест не пройден\n";
} else {
std::cout << "Тест пройден\n";
}
Идея простая: передаете значение вне допустимой области и проверяете, что исключение действительно было выброшено. В настоящих проектах для этого обычно используют тестовые фреймворки, но логика остается той же
Частые ошибки
Ждать, что математическая функция сама бросит исключение
Не стоит рассчитывать, что std::sqrt(-1) обязательно даст std::domain_error. Делайте явную проверку, если нужна предсказуемость
Ловить исключение по значению
Лучше ловить по константной ссылке:
catch (const std::domain_error& error)
Так не создается лишняя копия объекта исключения
Бросать domain_error вместо проверки логики
Исключение не должно заменять нормальную валидацию пользовательского ввода во всех местах. Если пользователь постоянно вводит данные, иногда удобнее показать сообщение и попросить повторить ввод
Короткий вывод
Чтобы спровоцировать domain_error в C++, используйте throw std::domain_error(...) внутри проверки аргументов. Это исключение подходит, когда значение не входит в допустимую область для вашей функции
Связанные материалы:
Что почитать дальше по C++
Если нужен общий маршрут по теме, откройте рубрику C++. Для соседних задач пригодятся эти разборы:
- Microsoft Visual C++ 14.0: что это, зачем нужен и как исправить ошибку установки
- C++ с нуля: первый файл, переменные и вывод
- Enter в C++: перенос строки, std::endl и ввод через cin
- Microsoft Visual C++ Runtime Library ошибка: как исправить без скачивания DLL