istringstream is(s);
T t;
if (!(is >> t)) throw bad_from_string();
return t;
}
Рассмотрим пример.
double d = from_string
void do_something(const string& s)
try
{
int i = from_string
// ...
}
catch (bad_from_string e) {
error ("Неправильная строка ввода",s);
}
Дополнительная сложность функции from_string()
по сравнению с функцией to_string()
объясняется тем, что класс string
может представлять значения многих типов. Это значит, что каждый раз мы должны указывать, какой тип значений хотим извлечь из объекта класса string
. Кроме того, это значит, что класс string
, который мы изучаем, может не хранить значение типа, который мы ожидаем. Рассмотрим пример.
int d = from_string
Итак, возможна ошибка, которую мы представили в виде исключения типа bad_from_string
. В разделе 23.9 мы покажем, что функция from_string()
(или эквивалентная) играет важную роль в серьезных текстовых приложениях, поскольку нам необходимо извлекать числовые значения из текстовых полей. В разделе 16.4.3 было показано, как эквивалентная функция get_int()
используется в графическом пользовательском интерфейсе.
Обратите внимание на то, что функции to_string()
и from_string()
очень похожи. Фактически они являются обратными друг другу; иначе говоря (игнорируя детали, связанные с пробелами, округлением и т.д.), для каждого “разумного типа T
” имеем
s==to_string(from_string
и
t==from_string
Здесь слово “разумный” означает, что тип T
должен иметь конструктор по умолчанию, оператор >>
и соответствующий оператор <<
.
to_string()
и from_string()
используют класс stringstream
для выполнения всей работы. Это наблюдение было использовано для определения универсальной операции конвертирования двух произвольных типов с согласованными операциями <<
и >>
.
struct bad_lexical_cast:std::bad_cast
{
const char* what() const { return "bad cast"; }
};
template
Target lexical_cast(Source arg)
{
std::stringstream interpreter;
Target result;
if (!(interpreter << arg) // записываем arg в поток
|| !(interpreter >> result) // считываем result из потока
|| !(interpreter >> std::ws).eof()) // поток пуст?
throw bad_lexical_cast();
return result;
}
Довольно забавно и остроумно, что инструкция !(interpreter>>std::ws).eof()
считывает любой пробел, который может остаться в потоке stringstream
после извлечения результата. Пробелы допускаются, но кроме них в потоке ввода может не остаться никаких других символов, и мы должны реагировать на эту ситуацию, как на обнаружение конца файла. Итак, если мы пытаемся считать целое число int
из объекта класса string
, используя класс lexical_cast
, то в результате выражения lexical_cast
и lexical_cast
будут считаться допустимыми, а выражение lexical_cast
— нет из-за последней пятерки.
Довольно элегантное, хотя и странное, имя lexical_cast
используется в библиотеке boost
, которую мы будем использовать для сравнения регулярных выражений в разделах 23.6–23.9. В будущем она станет частью новых версий стандарта языка С++ .
23.3. Потоки ввода-вывода
iostream
описана в главах 10-11, поэтому просто подведем итог.
Стандартные потоки организованы в виде иерархии классов (см. раздел 14.3).