#опытным
У std::variant довольно громоздкий интерфейс при возврате ошибки вместе с результатом работы функции. Но в С++17 появился еще один класс, который имеет семантику "Или" для типов + более дружелюбный интерфейс.
Это std::optional. Этот шаблонный класс либо содержит нужный тип, либо не содержит его. Вот так может выглядеть код:
struct Error {
std::string message;
};
std::optional<double> safe_divide(double a, double b) {
if (b == 0.0) { // здесь нужна нормальная проверка на равенство с epsilon
return std::nullopt;
}
return a / b;
}
auto div_result = safe_divide(10.0, 2.0);
if (div_result.has_value()) {
std::cout << "Result: " << div_result.value() << std::endl;
} else {
std::cout << "Error: there is no value" << std::endl;
}
// или с операторами
if (div_result) { // operator bool
std::cout << "Result: " << *div_result << std::endl; // operator*
} else {
std::cout << "Error: there is no value" << std::endl;
}Для того, чтобы вернуть пустой optional, используется константа std::nullopt. А в остальном интерфейс очень похож на std::expected за исключением доступа к ошибке.
Но на мой взгляд, std::optional не очень подходит для обработки ошибок.
👉🏿 Он имеет семантику наличия или отсутствия значения. Отсутствие значения - это в принципе нормальная ситуация в программировании. Вы сделали Select к базе и получили пустоту, запросили что-то по апи и получили пустоту - вот самое место для std::optional.
Получается, что вы в одном месте кодовой базы используете опшинал для простой индикации наличия результата, а в другом случае отсутствие значения означает ошибку. Это несколько сбивает с толку. Хочется использовать разные инструменты для обоих этих случаев.
👉🏿 Если вам нужно специфицировать, какая конкретно ошибка произошла, то std::optional умывает руки. Нужно либо output параметры использовать, либо в принципе другой класс.
Если есть 23-й стандарт или доступ к бусту, то лучше использовать std::expected или boost::outcome.
Use the right tool. Stay cool.
#cpp17