«Обнаружение есть или должно быть точной наукой...» Артур Конан Дойл
int i2 = convert<int>("not an int", cnv).value_or(-1);
if (i2 == -1) process_failure();
Приведенный выше код является простым и понятным, но, к сожалению, строго говоря, не является полностью детерминированным, поскольку -1 может быть результатом неудачи преобразования или успешного преобразования строки «-1». Тем не менее, на самом деле «запасные» значения (за пределами допустимого/разумного диапазона) довольно часто доступны для обозначения сбоев конверсии. Если это так, то такое простое развертывание может быть адекватным без введения недетерминированного поведения. В качестве альтернативы, может быть не так уж редко игнорировать сбои конверсии в целом и просто регистрировать сбой и продолжать поставляемое значение запаса.
Приложения, не относящиеся к этим категориям, по-прежнему требуют надежного обнаружения и обработки сбоев преобразования. Ответ<boost::lexical_cast>[только] заключается в том, чтобы бросить на неудачу иповысить.Конверттакже поддерживает это поведение:
try
{
int i1 = lexical_cast<int>(str);
int i2 = convert<int>(str, cnv).value();
}
catch (...)
{
process_failure();
}
Однако, чтобы удовлетворить более широкий диапазон вариаций программного потока,Boost.Convertдобавляет гибкость.
- отсрочить момент, когда исключение из-за отказа в преобразовании фактически отброшено или
- полностью избежать исключения.
optional<int> r1 = convert<int>(str1, cnv);
optional<int> r2 = convert<int>(str2, cnv);
try
{
int i1 = r1.value();
int i2 = r2.value();
}
catch (boost::bad_optional_access const&)
{
}
int i1 = r1 ? r1.value() : fallback_value;
int i2 = r2.value_or(fallback_value);
int i3 = convert<int>(str3, cnv).value_or(fallback_value);
int i4 = convert<int>(str3, cnv).value_or_eval(fallback_function);
Вот<boost::optional>шаги вперед, как фактический тип, возвращенный<boost::convert()>, которого мы до сих пор избегали, немедленно называя его методами доступа к стоимости:
int i1 = boost::convert<int>(str1, cnv).value();
int i2 = boost::convert<int>(str2, cnv).value_or(fallback_value);
int i3 = boost::convert<int>(str3, cnv).value_or_eval(fallback_function);
![[Note]](/img/note.png) | Note |
|---|
Одним из заметных преимуществ<value_or_eval()>перед<value_or()>является то, что фактический расчет<fallback_value>задерживается, обусловленный успехом или неудачей преобразования. |
С точки зрения пользователя,<boost::lexical_cast>обрабатывает сбой несколько одномерным необоротным способом.<boost::convert>Более гибкий подход. Он предоставляет выбор и оставляет решение пользователю. Не исключено, что на библиотечном уровне единственным доступным вариантом может быть распространение исключения из-за отказа от конверсии. На уровне приложений, хотя, по моему личному опыту, выбор в подавляющем большинстве случаев заключался в том, чтобы обрабатывать сбои конверсиилокально, то есть избегать распространения исключений с отказом конверсии или, что еще лучше, вообще избегать исключений с программными потоками, подобными:
boost::optional<int> res = boost::convert<int>(str, cnv);
if (!res) log("str conversion failed!");
int i1 = res.value_or(fallback_value);
и
struct fallback_func
{
int operator()() const { log("Failed to convert"); return 42; }
};
int i2 = convert<int>(str, cnv).value_or_eval(fallback_func());
int i3 = convert<int>(str, cnv, fallback_func());