7.14 Операции присваивания
Есть много операций присваивания, все группируют слева направо.
Все в качестве левого операнда требуют lvalue, и тип выражения
присваивания тот же, что и у его левого операнда. Это lvalue не
может ссылаться на константу (имя массива, имя функции или const).
Значением является значение, хранящееся в левом операнде просле
выполнения присваивания.
выражение_присваивания:
выражение операция_присваивания выражение
операция_присваивания: одна из
= += -= *= /= %= >>= <<= &= ~= |=
В простом присваивании с = значение выражения замещает собой
значение объекта, на который ссылается операнд в левой части. Если
оба операнда имеют арифметический тип, то при подготовке к
присваиванию правый операнд преобразуется к типу левого. Если
аргумент в левой части имеет указательный тип, аргумент в правой
части должен быть того же типа или типа, кторый может быть
преобразован к нему, см. #6.7. Оба операнда могут быть объектами
одного класса. Могут присваиваться объекты некоторых производных
классов; см. #8.5.3.
Присваивание объекту типа "указатель на ..." выполнит
присваивание объекту, денотируемому ссылкой.
Выполнение выражения вида E1 op= E2 можно представить себе как
эквивалентное E1 = E1 op (E2); но E1 вычисляется только один раз. В
+= и -= левый операнд может быть указателем, и в этом случае
(интегральный) правый операнд преобразуется так, как объяснялось в
#7.4; все правые операнды и не являющиеся указателями левые должны
иметь арифметический тип.
7.15 Операция запятая
запятая_выражение:
выражение , выражение
Пара выражений, разделенных запятой, вычисляется слева направо,
значение левого выражения теряется. Тип и значение результата
являются типом и значением правого операнда. Эта операция
группирует слева направо. В контексте, где запятая имеет
специальное значение, как например в списке фактических параметров
функции (#7.1) и в списке инициализаторов (#8.6), операция
запятая, как она описана в этом разделе, может появляться только в
скобках; например,
f (a,(t=3,t+2),c)
имеет три параметра, вторым из которых является значение 5.
- стр 273 -
7.16 Перегруженные операции
Большинство операций может быть перегружено, то есть, описано
так, чтобы они получали в качестве операндов объекты классов (см.
#8.5.11). Изменить приоритет операций невозможно. Невозможно
изменить смысл операций при применении их к неклассовым объектам.
Предопределенный смысл операций = и & (унарной) при применении их к
объектам классов может быть изменен.
Эквивалентность операций, применяемых к основным типам (например,
++a эквивалентно a+=1), не обязательно выполняется для операций,
применяемых к классовым типам. Некоторые операции, например,
присваивание, в случае применения к основным типам требуют, чтобы
операнд был lvalue; это не требуется для операций, описанных для
классовых типов.
7.16.1 Унарные операции
Унарная операция, префиксная или постфиксная, может быть
определена или с помощью функции члена (см. #8.5.4), не получающей
параметров, или с помощью функции друга (см. #8.5.10), получающей
один параметр, но не двумя способами одновременно. Так, для любой
унарной операции @, x@ и @x могут интерпретироваться как
x.операция@() или операция@(x). При перегрузке операций ++ и --
невозможно различить префиксное и постфиксное использование.
7.16.2 Бинарные операции
Бинарная операция может быть определена или с помощью функции
члена (см. #8.5.4), получающей один параметр, или с помощью функции
друга (см. #8.5.9), получающей два параметра, но не двумя способами
одновременно. Так, для любой бинарной операции @, x@y может быть
проинтерпретировано как x.операция@(y) или операция@(x,y).
7.16.3 Особые операции
Вызов функции
первичное_выражение ( список_выражений opt )
и индексирование
первичное_выражение [ выражение ]
считаются бинарными операциями. Именами определяющей функции
являются соответсвенно operator() и operator[]. Обращение x(arg)
интерпретируется как x.operator()(arg) для классового объекта x.
Индексирование x[y] интерпретируется как x.operator[](y).
- стр 274 -
8. ОПИСАНИЯ
Описания используются для определения интерпретации, даваемой
каждому идентификатору; они не обязательно резервируют память,
связанную с идентификатором. Описания имеют вид:
описание:
спецификаторы_описания opt список_описателей opt ;
описание_имени
asm_описание
Описатели в списке_описателей содержат идентификаторы, подлежащие
описанию. Спецификаторы_описания могут быть опущены только в
определениях внешних функций (#10) или в описаниях внешних
функций. Список описателей может быть пустым только при описании
класса (#8.5) или перечисления (#8.10), то есть, когда
спецификаторы_описания - это class_спецификатор или
enum_спецификатор. Описания имен описываются в #8.8; описания asm
описаны в #8.11.
спецификатор_описания:
sc_спецификатор
спецификатор_типа
фнк_спецификатор
friend
typedef
спецификаторы_описания:
спецификатор_описания спецификатор_описания opt
Список должен быть внутренне непротиворечив в описываемом ниже
смысле.
8.1 Спецификаторы класса памяти
Спецификаторы "класса памяти" (sc-спецификатор) это:
sc-спецификатор:
auto
static
extern
register
Описания, использующие спецификаторы auto, static и register
также служат определениями тем, что они вызывают резервирование
соответствующего объема памяти. Если описание extern не является
определением (#4.2), то где-то еще должно быть определение для
данных идентификаторов.
Описание register лучше всего представить как описание auto
(автоматический) с подсказкой компилятору, что описанные переменные
усиленно используются. Подсказка может быть проигнорирована. К ним
не может применяться операция получения адреса &.
Спецификаторы auto или register могут применяться только к
именам, описанным в блоке, или к формальным параметрам. Внутри
- стр 275 -
блока не может быть описаний ни статических функций, ни статических
формальных параметров.
В описании может быть задан максимум один sc_спецификатор. Если в
описании отсутсвует sc_спецификатор, то класс памяти принимается
автоматическим внутри функции и статическим вне. Исключение:
функции не могут быть автоматическими.
Спецификаторы static и extern могут использоваться только для
имен объектов и функций.
Некоторые спецификаторы могут использоваться только в описаниях
функций:
фнк-спецификатор:
overload
inline
virtual
Спецификатор перегрузки overload делает возможным использование
одного имени для обозначения нескольких функций; см. #8.9.
Спецификатор inline является только подсказкой компилятору, не
влияет на смысл программы и может быть проигнорирован. Он
используется, чтобы указать на то, что при вызове функции inline-
подстановка тела функции предпочтительнее обычной реализацци вызова
функции. Функция (#8.5.2 и #8.5.10), определенная внутри описания
класса, является inline по умолчанию.
Спецификатор virtual может использоваться только в описаниях
членов класса; см. #8.5.4.
Спецификатор friend используется для отмены правил скрытия имени
для членов класса и может использоваться только внутри описаний
классов; см. #8.5.9.
С помощью спецификатора typedef вводится имя для типа; см. #8.8.
8.2 Спецификаторы Типа
Спецификаторами типов (спецификатор_типа) являются:
спецификатор_типа:
простое_имя_типа
class_спецификатор
enum-спецификатор
сложный_спецификатор_типа
const
Слово const можно добавлять к любому допустимому
спецификатору_типа. В остальных случаях в описании может быть дано
не более одного спецификатора_типа. Объект типа const не является
lvalue. Если в описании опущен спецификатор типа, он принимается
int.
- стр 276 -
простое_имя_типа:
char
short
int
long
unsigned
float
double
const
void
Слова long, short и unsigned можно рассматривать как
прилагательные. Они могут применяться к типу int; unsigned может
также применяться к типам char, short и long.
Спецификаторы класса и перечисления обсуждаются в #8.5 и #8.10
соответственно.
сложный_спецификатор_типа:
ключ typedef-имя
ключ идентификатор
ключ:
class
struct
union
enum
Сложный спецификатор типа можно использовать для ссылки на имя
класса или перечисления там, где имя может быть скрыто локальным
именем. Например:
class x { ... };
void f(int x)
{
class x a;
// ...
}
Если имя класса или перечисления ранее описано не было,
сложный_спецификатор_типа работает как описание_имени; см. #8.8.
8.3 Описатели
Список_описателей, появляющийся в описании, есть разделенная
запятыми последовательность описателей, каждый из которых может
иметь инициализатор.
список_описателей:
иниц_описатель
иниц_описатель , список_описателей
- стр 277 -
иниц_описатель:
описатель инициализатор opt
Инициализаторы обсуждаются в #8.6. Спецификатор в описании
указывает тип и класс памяти объектов, к которым относятся
описатели. Описатели имеют синтаксис:
описатель:
оп_имя
( описатель )
* const opt описатель
& const opt описатель
описатель ( список_описаний_параметров )
описатель [ константное_выражение opt ]
оп-имя:
простое_оп_имя
typedef-имя :: простое_оп_имя
простое_оп_имя:
идентификатор
typedef-имя
~ typedef-имя
имя_функции_операции
имя_функции_преобразования
Группировка та же, что и в выражениях.
8.4 Смысл описателей
Каждый описатель считается утверждением того, что если в
выражении возникает конструкция, имеющаяя ту же форму, что и
описатель, то она дает объект указанного типа и класса памяти.
Каждый описатель содержит ровно одно оп_имя; оно определяет
описываемый идентификатор. За исключеним описаний некоторых
специальных функций (см. #8.5.2) , оп_имя будет простым
идентификатором.
Если в качестве описателя возникает ничем не снабженный
идентификатор, то он имеет тип, указанный спецификатором,
возглавляющим описание.
Описатель в скобках эквивалентен описателю без скобок, но связку
сложных описателей скобки могут изменять.
Теперь представим себе описание
T D1
где T - спецификатор типа (как int и т.д.), а D1 - описатель.
Допустим, что это описание заставляет идентификатор иметь тип "...
T", где "..." пусто, если идентификатор D1 есть просто обычый
идентификатор (так что тип x в "int x" есть просто int). Тогда,
если D1 имеет вид
*D
- стр 278 -
то тип содержащегося идентификатора есть "... указатель на T."
Если D1 имеет вид
* const D
то тип содержащегося идентификатора есть "... константный указатель
на T", то есть, того же типа, что и *D, но не lvalue.
Если D1 имеет вид
&D
или
& const D
то тип содержащегося идентификатора есть "... ссылка на T."
Поскольку ссылка по определению не может быть lvalue, использование