Литмир - Электронная Библиотека
A
A
Программирование. Принципы и практика использования C++ Исправленное издание - _358.png
Программирование. Принципы и практика использования C++ Исправленное издание - _359.png
Программирование. Принципы и практика использования C++ Исправленное издание - _360.png
Программирование. Принципы и практика использования C++ Исправленное издание - _361.png

См. раздел А.5.5.

Программирование. Принципы и практика использования C++ Исправленное издание - _362.png

Рассмотрим пример.

template<class T> T& max(T& a, T& b) { return (a>b)?a:b; }

Оператор “знак вопроса” описан в разделе 8.4.

Программирование. Принципы и практика использования C++ Исправленное издание - _363.png

Фраза “аналог

v=v*(x)
” означает, что значение выражения
v*=x
совпадает со значением выражения
v=v*(x)
, за исключением того, что значение v вычисляется только один раз. Например, выражение
v[++i]*=7+3
означает
(++i, v[i]=v[i]*(7+3)
), а не (
v[++i]=v[++i]*(7+3)
) (которое может быть неопределенным; см. раздел 8.6.1).

Программирование. Принципы и практика использования C++ Исправленное издание - _364.png

Результат выражения

throw
имеет тип
void
.

Программирование. Принципы и практика использования C++ Исправленное издание - _365.png

Каждая таблица содержит операторы, имеющие одинаковый приоритет. Операторы в более высоко расположенных таблицах имеют более высокий приоритет по сравнению с операторами, расположенными ниже. Например, выражение

a+b*c
означает
a+(b*c)
, а не
(a+b)*c
, поскольку оператор
*
имеет более высокий приоритет по сравнению с оператором
+
. Аналогично, выражение
*p++
означает
*(p++)
, а не
(*p)++
. Унарные операторы и операторы присваивания являются правоассоциативными (right-associative); все остальные — левоассоциативными. Например, выражение
a=b=c
означает
a=(b=c)
, а выражение
a+b+c
означает
(a+b)+c
.
Lvalue
— это объект, допускающий модификацию. Очевидно, что объект
lvalue
, имеющий спецификатор
const
, защищен от модификации системой типов и имеет адрес. Противоположностью выражения
lvalue
является выражение
rvalue
, т.е. выражение, идентифицирующее нечто, что не может быть модифицировано или не имеет адреса, например, значение, возвращаемое функцией (
&f(x)
— ошибка, поскольку значение, возвращаемое функцией
f(x)
, является значением
rvalue
).

A.5.1. Операторы, определенные пользователем

Правила, перечисленные выше, установлены для встроенных типов. Если же используется оператор, определенный пользователем, то выражение просто преобразовывается в вызов соответствующей операторной функции, определенной пользователем, и порядок действий определяется правилами, установленными для вызова функций. Рассмотрим пример.

class Mine { /* .. . */ };

bool operator==(Mine, Mine);

void f(Mine a, Mine b)

{

  if (a==b) { // a==b означает operator==(a,b)

    // ...

  }

}

Тип, определенный пользователем, — это класс (см. главу 9, раздел A.12) или перечисление (см. разделы 9.5, A.11).

A.5.2. Неявное преобразование типа

Целочисленные типы или типы с плавающей точкой (раздел A.8) могут свободно смешиваться в операторах присваивания и в выражениях. При первой же возможности значения преобразовываются так, чтобы не потерять информацию. К сожалению, преобразования, уничтожающие значение, выполняются также неявно.

A.5.2.1. Продвижения

Неявные преобразования, сохраняющие значения, обычно называют продвижениями (promotions). Например, перед выполнением арифметической операции для создания типа int из более коротких целочисленных типов выполняется целочисленное продвижение (integral promotion). Это отражает исходную цель продвижений: привести операнды арифметических операций к “естественным” размерам. Кроме того, преобразование значения типа

float
в значение типа
double
также считается продвижением.

Продвижения используются как часть обычных арифметических преобразований (раздел A.5.2.2).

A.5.2.2. Преобразования

Значения фундаментальных типов можно преобразовывать друг в друга самыми разными способами. При написании программы следует избегать неопределенного поведения и непредсказуемых преобразований, которые незаметно искажают информацию (см. разделы 3.9 и 25.5.3). Компиляторы обычно способны предупредить о многих сомнительных преобразованиях.

Целочисленные преобразования. Целое число может быть преобразовано в другой целый тип. Значение перечисления может быть преобразовано в целый тип. Если результирующим типом является тип без знака (

unsigned
), то результирующее значение будет иметь столько же битов, сколько и источник, при условии, что оно может поместиться в целевой области памяти (старшие биты при необходимости могут быть отброшены). Если целевой тип имеет знак, то значение останется без изменения, при условии, что его можно представить с помощью целевого типа; в противном случае значение определяется реализацией языка. Обратите внимание на то, что типы
bool
и
char
являются целочисленными.

Преобразования значений с плавающей точкой. Значение с плавающей точкой можно преобразовать в значение с плавающей точкой другого типа. Если исходное значение можно точно представить с помощью целевого типа, то результатом будет исходное числовое значение. Если же исходное значение лежит между двумя целевыми значениями, то результатом будет одно из этих значений. Иначе говоря, результат непредсказуем. Обратите внимание на то, что преобразование значения типа float в значение типа

double
считается продвижением.

Преобразование указателей и ссылок. Любой указатель на тип объекта можно преобразовать в указатель типа

void*
(см. разделы 17.8 и 27.3.5). Указатель (ссылка) на производный класс можно неявно преобразовать в указатель (ссылку) на доступный и однозначно определенный базовый класс (см. раздел 14.3). Константное выражение (см. разделы A.5 и 4.3.1), равное нулю, можно неявно преобразовать в любой другой тип указателя. Указатель типа
T*
можно неявно преобразовать в указатель
const T*
. Аналогично ссылку
T&
можно неявно преобразовать в ссылку типа
const T&
.

408
{"b":"847443","o":1}