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.
Синтаксис numeric_cast
очень прост. Это шаблон функции, объявленный следующим образом.
template<typename Target, typename Source>
inline Target numeric_cast(Source arg)
Если вы уже прочли рецепты 3.1 и 3.3, он аналогичен lexical_cast
. У него имеется два параметра шаблона — Target
и Source
, — которые представляют типы оригинального и результирующего значений. Так как это шаблон функции, компилятор может догадаться о типе аргумента Source
, так что требуется указать только Target
, как здесь.
int i = 32767;
short s = numeric_cast<short>(i);
short
— это аргумент, передаваемый в шаблон как параметр Target
. Компилятор догадывается, что Source
имеет тип int
потому, что i
имеет тип int
.
В этом случае я впихиваю int
в short
. В моей системе (Windows XP) int имеет длину четыре байта, a short
— два. short
имеет знак, это означает, что для представления числа в нем используется 15 бит и, следовательно, максимальным допустимым положительным значением для него является 32 767. Приведенный выше фрагмент кода работает молча, но когда я увеличиваю i
на единицу, она выходит за диапазон short
.
s = numeric_cast<short>(i); // Ох!
Вы уже догадались, что выбрасывается исключение bad_numeric_cast
. Смотри остальную часть примера 3.8: numeric
_cast также перехватывает потери знака, возникающие при присвоении отрицательного значения со знаком типу без знака.
Но numeric_cast
не решает всех проблем. Если попытаться поместить значение с плавающей точкой в тип без плавающей точки, то будет потеряно все, что находится справа от десятичной точки, так? numeric_cast
в этой ситуации не спасает, так что не думайте, что он сможет уберечь вас от всех рискованных предприятий. Например, рассмотрим такой фрагмент кода из примера 3.8:
double a = 3.14;
int i = numeric_cast<int>(d); // Ох!
Здесь не будет выброшено никаких исключений. Но это произойдет, если попробовать такое:
double d = -3.14;
unsigned int ui = numeric_cast<unsigned int>(d);
Потому что, несмотря на то что происходит потеря всего, что находится справа от десятичной точки, происходит потеря знака, а это очень плохо.
Рецепты 3.1 и 3.3.
3.7. Получение минимального и максимального значений числового типа
Требуется узнать наибольшее и наименьшее значения, представляемые на данной платформе числовым типом, таким как int
или double
.
Чтобы среди прочего получить максимальное и минимальное допустимые значения числового типа, используйте шаблон класса numeric_limits
из заголовочного файла <limits>
(см. пример 3.9).
Пример 3.9. Получение числовых ограничений
#include <iostream>
#include <limits>
using namespace std;
template<typename T>
void showMinMax() {
cout << "min: " << numeric_limits<T>::min() << endl;
cout << "max: " << numeric_limits<T>::max() << endl;
cout << endl;
}
int main() {
cout << "short:" << endl;
showMinMax<short>();
cout << "int:" << endl;
showMinMax<int>();
cout << "long:" << endl;
showMinMax<long>();
cout << "float:" << endl;
showMinMax<float>();
cout << "double:" << endl;
showMinMax<double>();
cout << "long double:" << endl;
showMinMax<long double>();
cout << "unsigned short:" << endl;
showMinMax<unsigned short>();
cout << "unsigned int:" << endl;
showMinMax<unsigned int>();
cout << "unsigned long:" << endl;
showMinMax<unsigned long>();
}
Вот что я получил в Windows XP, используя Visual C++ 7.1.
short:
min: -32768
max: 32767