В примере 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("7.0");

float f = strToNum("7.0");

int i = strToNum("7.0");

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

Смотри также

Рецепт 3.2.

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

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

Решение

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

Пример 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(i);

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

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

  s = numeric__cast(i);

 } catch (bad_numeric_cast& e) {

  cerr << e.what() << endl;

 }

 try {

  int i = 300;

  unsigned int ui = numeric_cast(i);

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

  i *= -1;

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

 } catch (bad_numeric_cast& e) {

  cerr << e.what() << endl;

 }

 try {

  double d = 3.14.

  int i = numeric_cast(d);

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

Перейти на страницу:

Похожие книги