В примере 3.7 я написал простую функцию sciToDub
string
и возвращающую содержащийся в ней double
, если он допустим. В sciToDub
я использую stringstream
следующим образом.stringstream ss(str); // Конструирование из строки типа string
double d = 0;
ss >> d;
if (ss.fail()) {
string s = "Невозможно отформатировать ";
s += str;
s += " как число!";
throw (s);
}
return (d);
Наиболее важной частью здесь является то, что все, что требуется сделать, — это использовать для чтения из строкового потока в double
>>
), как это делается при чтении из cin
.Ну, это не совсем
stringstream
записано значение, которое не может быть записано в переменную в правой части оператора >>
, то для потока будет выставлен бит fail
. Этот бит можно проверить с помощью функции-члена fail
(на самом деле это функция-член basic_ios
, который является родительским классом для stringstream
). Кроме того, переменная справа от оператора >>
в случае ошибки значения не меняет.Однако с целью обобщения можно избежать написания отдельных версий sciToDub
int
, float
, double
и чего-либо еще, что может потребоваться преобразовать, если написать шаблон функции. Рассмотрим такую новую версию.template
T strToNum(const string& str) {
stringstream ss(str);
T tmp;
ss >> tmp;
if (ss.fail()) {
string s = "Невозможно отформатировать ";
s += str;
s += " как число!";
throw (s);
}
return (tmp);
}
Теперь, чтобы преобразовать string
double d = strToNum
float f = strToNum
int i = strToNum
Также параметром шаблона можно сделать тип символов, но это очень просто сделать, так что я оставляю это в качестве вашего упражнения.
Рецепт 3.2.
3.6. Преобразования между числовыми типами
Имеется число одного типа и требуется преобразовать его в другой, как int
short
или наоборот, но при этом необходимо перехватывать все ошибки переполнения (overflow) или потери значимости (underflow), возникающие при работе программы.Используйте шаблон класса numeric_cast
bad_numeric_cast
. Пример 3.8 показывает, как это выполняется.#include
#include
using namespace std;
using boost::numeric_cast;
using boost::bad_numeric_cast;
int main() {
// Целые типы
try {
int i = 32767;
short s = numeric_cast
cout << "s = " << s << endl;
i++; // Теперь i выходит за диапазон (если sizeof(short) равен 2)
s = numeric__cast
} catch (bad_numeric_cast& e) {
cerr << e.what() << endl;
}
try {
int i = 300;
unsigned int ui = numeric_cast
cout << ui << endl; // Прекрасно
i *= -1;
ui = numeric_cast
} catch (bad_numeric_cast& e) {
cerr << e.what() << endl;
}
try {
double d = 3.14.
int i = numeric_cast
i = numeric_cast
cout << i << endl; // i = 3
} catch (bad_numeric_cast& e) {
cerr << e.what( ) << endl;
}
}
Вы, вероятно, знаете, что базовые типы C++ имеют различные размеры. Стандарт C++ содержит жесткие указания по относительному размеру типов: int
short int
, но он не указывает абсолютных размеров. Это означает, что если взять long int
и попытаться записать его значение в short
или попытаться поместить int
в unsigned int
, то информация о значении переменной-источника, такая как знак или даже часть числового значения, может быть потеряна.