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).