Язык программирования C++. Пятое издание - Стенли Липпман
Шрифт:
Интервал:
Закладка:
При разных знаках, если тип беззнакового операнда больший, чем у знакового операнда, знаковый операнд преобразуется в беззнаковый. Например, при операторах типа unsigned int и int, int преобразуется в unsigned int. Следует заметить, что если значение типа int отрицательное, результат преобразуется так, как описано в разделе 2.1.2.
Остается случай, когда тип знакового операнда больше, чем беззнакового. В данном случае результат зависит от конкретной машины. Если все значения беззнакового типа соответствуют большему типу, то операнд беззнакового типа преобразуется в знаковый. Если значения не соответствуют, то знаковый операнд преобразуется в беззнаковый. Например, если операнды имеют типы long и unsigned int и размер у них одинаковый, операнд типа long будет преобразован в unsigned int. Если тип long окажется больше, то unsigned int будет преобразован в long.
Концепция арифметических преобразованийАрифметические преобразования проще всего изучить на примерах.
bool flag; char cval;
short sval; unsigned short usval;
int ival; unsigned int uival;
long lval; unsigned long ulval;
float fval; double dval;
3.14159L + 'a'; // 'a' преобразуется в int, а затем int в long double
dval + ival; // ival преобразуется в double
dval + fval; // fval преобразуется в double
ival = dval; // dval преобразуется в int (с усечением)
flag = dval; // если dval - 0, flag - false, в противном случае - true
cval + fval; // cval преобразуется в int, затем int во float
sval + cval; // sval и cval преобразуется в int
cval + lval; // cval преобразуется в long
ival + ulval; // ival преобразуется в unsigned long
usval + ival; // преобразование зависит от соотношения
// размеров типов unsigned short и int
uival + lval; // преобразование зависит от соотношения
// размеров типов unsigned int и long
В первом выражении суммы символьная константа 'a' имеет тип char, являющийся числовым (см. раздел 2.1.1). Какое именно это значение, зависит от используемого машиной набора символов. На машине авторов, где установлен набор символов ASCII, символу 'a' соответствует число 97. При добавлении символа 'a' к значению типа long double значение типа char преобразуется в тип int, а затем в тип long double. Это преобразованное значение добавляется к литералу. Интересны также два последних случая, где происходит преобразование беззнаковых значений. Тип результата этих выражений зависит от конкретной машины.
Упражнения раздела 4.11.1Упражнение 4.34. С учетом определений переменных данного раздела объясните, какие преобразования имеют место в следующих выражениях:
(a) if (fval) (b) dval = fval + ival; (c) dval + ival * cval;
Помните, что возможно придется учитывать порядок операторов.
Упражнение 4.35. С учетом определений
char cval; int ival; unsigned int ui;
float fval; double dval;
укажите неявные преобразования типов, если таковые вообще имеются.
(a) cval = 'a' + 3; (b) fval = ui - ival * 1.0;
(с) dval = ui * fval; (d) cval = ival + fval + dval;
4.11.2. Другие неявные преобразования
Кроме арифметических, существует еще несколько видов неявных преобразований, включая следующие.
Преобразование массива в указатель. В большинстве выражений, когда используется массив, он автоматически преобразуется в указатель на свой первый элемент.
int ia[10]; // массив из десяти целых чисел
int* ip = ia; // ia преобразуется в указатель на первый элемент
Это преобразование не происходит при использовании массива в выражении decltype или в качестве операнда операторов обращения к адресу (&), sizeof или typeid (который рассматривается в разделе 19.2.2). Преобразование не происходит также при инициализации ссылки на массив (см. раздел 3.5.1). Подобное преобразование указателя происходит при использовании в выражении типа функции, как будет описано в разделе 6.7.
Преобразование указателя. Существует несколько других преобразований указателя: постоянное целочисленное значение 0 и литерал nullptr могут быть преобразованы в указатель на любой тип; указатель на любой неконстантный тип может быть преобразован в void*, а указатель на любой тип может быть преобразован в const void*. Как будет продемонстрировано в разделе 15.2.2, существуют дополнительные преобразования указателя, относящиеся к типам, связанным наследованием.
Преобразование в тип bool. Существует автоматическое преобразование арифметических типов и типов указателя в тип bool. Если указатель или арифметическое значение — нуль, преобразование возвращает значение false; любое другое значение возвращает true:
char *cp = get_string();
if (cp) /* ... */ // true, если cp не нулевой указатель
while (*cp) /* ... */ // true, если *cp не нулевой символ
Преобразование в константу. Указатель на неконстантный тип можно преобразовать в указатель на соответствующий константный тип, то же относится и к ссылкам. Таким образом, если Т — тип, то указатель или ссылку на тип T можно преобразовать в указатель или ссылку на const Т (см. разделы 2.4.1 и 2.4.2).
int i;
const int &j = i; // преобразовать в ссылку на const int
const int *p = &i; // преобразовать неконстантный адрес в константный
int &r = j, *q = p; // ошибка: преобразование константы в не константу
// недопустимо
Обратное преобразование (устранение спецификатора const нижнего уровня) невозможно.
Преобразование, определенное типами класса. Тип класса может сам определять преобразования, которые компилятор применит автоматически. Компилятор применяет только одно преобразование типа класса за раз. В разделе 7.5.4 приведен пример, когда необходимо несколько преобразований, и он не работает.
В программах ранее уже использовались преобразования типов класса, когда символьная строка в стиле С использовалась там, где ожидался библиотечный тип string (см. раздел 3.5.5), а также при чтении из потока istream в условии.
string s, t = "a value"; // символьный строковый литерал преобразован
// в тип string
while (cin >> s) // условие while преобразует cin в bool
Условие (cin >> s) читает поток cin и возвращает его же как результат. Условия ожидают значение типа bool, но оно проверяет значение типа istream. Библиотека IO определяет преобразование из типа istream в bool. Это преобразование используется автоматически, чтобы преобразовать поток cin в тип bool. Результирующее значение типа bool зависит от состояния потока. Если последнее чтение успешно, то преобразование возвращает значение true. Если последняя попытка потерпела неудачу, то преобразование возвращает значение false.
4.11.3. Явные преобразования
Иногда необходимо явно преобразовать объект в другой тип. Например, в следующем коде может понадобиться использование деления с плавающей точкой:
int i, j;
double slope = i/j;
Для этого необходим способ явного преобразования переменных i и/или j в тип double. Для явного преобразования используется приведение (cast) типов.
Хотя приведение время от времени необходимо, оно довольно опасно.
Именованные операторы приведенияИменованный оператор приведения имеет следующую форму:
имя_приведения<тип>(выражение);
где тип — это результирующий тип преобразования, а выражение — приводимое значение. Если тип — ссылка, то результат l-значение. Имя_приведения может быть одним из следующих: static_cast, dynamic_cast, const_cast и reinterpret_cast. Приведение dynamic_cast, обеспечивающее идентификацию типов времени выполнения, рассматривается в разделе 19.2. Имя_приведения определяет, какое преобразование осуществляется.
Оператор static_castЛюбое стандартное преобразование типов, кроме задействующего спецификатор const нижнего уровня, можно затребовать, используя оператор static_cast. Например, приведя тип одного из операндов к типу double, можно заставить выражение использовать деление с плавающей точкой: