Давайте напишем и используем функцию преобразователя, которая преобразует<std::string
>в<int
>. Возможно, что для данной строки (например,<"cat"
>) не существует значения типа<int
>, способного представлять результат преобразования. Мы не считаем такую ситуацию ошибкой. Мы ожидаем, что конвертер можно использовать только для проверки возможности преобразования. Естественным признаком этой функции может быть:
#include <boost/optional.hpp>
boost::optional<int> convert(const std::string& text);
Вся необходимая функциональность может быть включена с одним заголовком<<boost/optional.hpp>
>. Вышеупомянутая подпись функции означает, что функция может либо возвращать значение типа<int
>, либо флаг, указывающий, что значение<int
>недоступно. Это не указывает на ошибку. Это как дополнительное значение<int
>. Вот как мы можем использовать нашу функцию:
const std::string& text = ;
boost::optional<int> oi = convert(text);
if (oi)
int i = *oi;
Чтобы проверить, содержит ли<optional
>значение, мы используем контекстное преобразование в тип<bool
>. Благодаря этому мы можем объединить инициализацию необязательного объекта и тест в одну инструкцию:
if (boost::optional<int> oi = convert(text))
int i = *oi;
Мы извлекаем содержащееся значение<operator*
>(и<operator->
>, где это имеет смысл). Попытка извлечь содержащееся значение неинициализированного необязательного объекта являетсянеопределенным поведением(UB). Эта реализация охраняет призыв<BOOST_ASSERT
>. Поэтому вы должны быть уверены, что содержащаяся ценность есть, прежде чем извлекать. Например, следующий код достаточно безопасен для UB:
int i = *convert("100");
Это потому, что мы знаем, что значение строки<"100"
>превращается в действительное значение<int
>. Если вам не нравится этот потенциальный UB, вы можете использовать альтернативный способ извлечения содержащегося значения:
try {
int j = convert(text).value();
}
catch (const boost::bad_optional_access&) {
}
Эта версия делает исключение при попытке получить доступ к несуществующему содержащемуся значению. Если ваш способ справиться с недостающим значением заключается в использовании некоторого по умолчанию, например<0
>, существует еще одна альтернатива:
int k = convert(text).value_or(0);
Это использует<atoi
>-подобный подход к конверсиям: если<text
>не представляет собой интегральное число, просто возвращайте<0
>. Наконец, вы можете предоставить обратный вызов при попытке получить доступ к содержащемуся значению:
int fallback_to_default()
{
cerr << "could not convert; using -1 instead" << endl;
return -1;
}
int l = convert(text).value_or_eval(fallback_to_default);
Это вызовет обратный вызов и вернет все, что вернет обратный вызов. Обратный вызов может иметь побочные эффекты: они будут наблюдаться только тогда, когда необязательный объект не содержит значения.
Теперь рассмотрим, как можно реализовать функцию<convert
>.
boost::optional<int> convert(const std::string& text)
{
std::stringstream s(text);
int i;
if ((s >> i) && s.get() == std::char_traits<char>::eof())
return i;
else
return boost::none;
}
Обратите внимание на два заявления о возвращении.<return
i
>использует преобразующий конструктор, который может создавать<optional<T>
>из<T
>. Таким образом, построенный факультативный объект инициализируется и его значение является копией<i
>. Другая обратная запись использует другой преобразующий конструктор из специального тега<boost::none
>. Он используется для указания на то, что мы хотим создать неинициализированный необязательный объект.