Как записать численно в символ? Правильно, что бы обнаружить переполнения?
ПОЛНОСТЬЮ ВОССТАНОВИЛ ВОПРОС.
Я хотел бы написать численно в целочисленный тип, не будучи уверенным, действительно ли это тип символа: char
, wchar_t
, char32_t
и т.п.
Рассмотрим следующий код:
template <typename T> class MyInteger
{
T value;
public:
MyInteger() : value{0} {}
friend istream &operator >> (istream &in, MyInteger &i)
{
in >> i.value;
return in;
}
friend ostream &operator << (ostream &out, const MyInteger &i)
{
out << i.value;
return out;
}
};
Я хочу, чтобы такой класс правильно обрабатывал все целочисленные типы, включая, например, int_least8_t
который может (если не гарантирован) оценить char
,
Приведенный выше код не работает с этой целью. Давайте предположим, что параметр шаблона T
установлен в char
(например, позволяя компилятору оценить int_least8_t
). Предоставление этой программы ввода 25
приведет к персонажу 5
оставаясь во входном потоке, и value
будет присвоен код ASCII2
(что бывает 50
). Это явно неправильно. Точно так же, заставляя эту программу печатать value
из 50
приведет к 2
печатается вместо.
Одним из очевидных решений является Typecast value
в int
в перегруженном >>
а также <<
операторы. Но опять же, что если T
установлен в int_least64_t
? Ну, мы могли бы Typecast value
в int_64least_t
вместо этого, но что если T
установлен в uint_least64_t
? Аналог, типографское оформление value
в uint_least64_t
создаст проблемы, если T
установлен в int_least64_t
, Не говоря уже о том, что реализации могут определять свои собственные, даже большие целые числа. В таком случае intmax_t
а также uintmax_t
может быть полезным, но только если T
не установлен еще больший, пользовательский целочисленный тип.
Тут приходит с помощью одинарного +
оператор. AFAIK это предназначено для преобразования целого числа символа в нормальное целое число, так что, например, cout
относится к этому должным образом. И да, с оператором <<
это работает аккуратно: out << +i.value;
Тем не менее, делать то же самое с оператором >>
гораздо страшнее Просто пишу in >> +i.value;
создает неприятные ошибки компиляции. На самом деле, я не уверен, почему. Я должен использовать фиктивную переменную:
auto hlp = +i.value;
in >> hlp;
i.value = hlp;
Это безобразно Но что еще хуже, это может не проверить правильность диапазона. Если istream
записывает в целочисленное значение, а затем в соответствии с http://www.cplusplus.com/reference/locale/num_get/get/ должен проверить, находится ли записанное значение в диапазоне. Если это не так, failbit
установлен, и переменной, в которую была записана, даны стандартные определенные значения. Поэтому целочисленных переполнений не возникает. Здесь, однако, у меня нет гарантии, что auto hlp
будет иметь тип точного размера типа i.value
, Продолжая int_least8_t
пример, hlp
будет повышен до типа int
который больше чем char
; поэтому, кормление программы с вводом, например, 900
приведет к value
устанавливается на -124
,
Конечно, мы могли бы продолжить проверку диапазона, проверив hlp
против numeric_limits<T>::lowest()
а также numeric_limits<T>::max()
, Однако это возможно только в том случае, если T
настроен на фундаментальные типы. Это не удастся для пользовательских целочисленных типов, поскольку в соответствии с http://www.cplusplus.com/reference/limits/numeric_limits/ numeric_limits
не может быть установлен для пользовательских типов.
Я согласен, что это очень сложная проблема. Но в этом суть - просто для упражнения я хотел бы быть как можно более перфекционистским.