Чтение онлайн

ЖАНРЫ

C++. Сборник рецептов

Когсуэлл Джефф

Шрифт:

В примере 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<typename T>

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<double>("7.0");

float f = strToNum<float>("7.0");

int i = strToNum<int>("7.0");

Также параметром шаблона можно сделать тип символов, но это очень просто сделать, так что я оставляю это в качестве вашего упражнения.

Смотри также

Рецепт 3.2.

3.6. Преобразования между числовыми типами

Проблема

Имеется число одного типа и требуется преобразовать его в другой, как

int
в
short
или наоборот, но при этом необходимо перехватывать все ошибки переполнения (overflow) или потери значимости (underflow), возникающие при работе программы.

Решение

Используйте шаблон класса

numeric_cast
Boost. Он выполняет проверки, которые при переполнениях переменной, принимающей значение, или других ошибках выбрасывают
исключение типа
bad_numeric_cast
. Пример 3.8 показывает, как это выполняется.

Пример 3.8. Безопасное преобразование чисел

#include <iostream>

#include <boost/cast.hpp>

using namespace std;

using boost::numeric_cast;

using boost::bad_numeric_cast;

int main {

 // Целые типы

 try {

int i = 32767;

short s = numeric_cast<short>(i);

cout << "s = " << s << endl;

i++; // Теперь i выходит за диапазон (если sizeof(short) равен 2)

s = numeric__cast<short>(i);

 } catch (bad_numeric_cast& e) {

cerr << e.what << endl;

 }

 try {

int i = 300;

unsigned int ui = numeric_cast<unsigned int>(i);

cout << ui << endl; // Прекрасно

i *= -1;

ui = numeric_cast<unsigned int>(i); // i отрицателен!

 } catch (bad_numeric_cast& e) {

cerr << e.what << endl;

 }

 try {

double d = 3.14.

int i = numeric_cast<int>(d);

i = numeric_cast<int>(d); // Это отрезает 0.14!

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
, то информация о значении переменной-источника, такая как знак или даже часть числового значения, может быть потеряна.

Только знания, что это может привести к проблемам, не достаточно. Вы можете быть ограничены жесткими требованиями по объему и не захотите использовать четыре байта для

long
, когда можно обойтись двумя байтами для
short
(если ваша платформа на самом деле использует такие размеры, что очень распространено, но не гарантируется). Из-за ограничений по объему может возникнуть желание попробовать хранить значения в наименьших возможных типах. Если вы любите приключения, но вам нужна страховка, для перехвата потерь данных при работе программы используйте
numeric_cast
из Boost.

Поделиться с друзьями: