Бьeрн Страустрап. Справочное руководство по C++ --------------------------------------------------------------- Второе дополненное издание ---------------------------------------------------------------  * СПРАВОЧНОЕ РУКОВОДСТВО R.1 Введение Это справочное руководство описывает язык программирования С++ по состоянии на май 1991. С++ - язык программирования общего назначения, базирующийся на языке программирования С Ь. Ь "The C Programming Language" B. Kernighan, D. Ritchie. Prentice Hall, 1978, 1988. Есть русский перевод: "Язык программирования С. Задачи по языку С" Б. Керниган, Д. Ритчи, А. Фьюер. "Финансы и статистика". 1984 В дополнение к возможностям С язык С++ предоставляет классы, функции подстановки, перегрузку операций, перегрузку имен функций, постоянные типы, ссылки, операторы управления свободной памятью, проверку параметров функций и приведение типов. Все расширения С суммируются в $$R.18.1. Различия между С++ и ANSI C++ приведены в $$R.18.2 Ь. Ь American National Standard X3.159-1989. Американский национальный стандарт. Расширения С++ версии 1985 года до данного описания суммируются в $$R.18.1.2. Разделы, относящиеся к шаблонам типа ($$R.14) и управлению особыми ситуациями ($$R.15), являются местами планируемых расширений языка. R.1.1 Обзор Это руководство содержит следующее: 1. Введение. 2. Соглашения о лексических понятиях. 3. Основные понятия. 4. Стандартные преобразования. 5. Выражения. 6. Операторы. 7. Описания. 8. Описатели. 9. Классы. 10. Производные классы. 11. Контроль доступа к членам. 12. Специальные функции-члены. 13. Перегрузка. 14. Шаблоны типов. 15. Управление особыми ситуациями. 16. Препроцессорная обработка. Приложение A: Сводка синтаксиса Приложение B: Совместимость R.1.2 Запись синтаксиса В записи синтаксиса языка в этом руководстве синтаксические понятия обозначаются курсивом, а литеральные слова и символы шрифтом постоянной ширины. Варианты перечисляются на отдельных строках, за исключением тех немногих случаев, когда длинный список вариантов дается на одной строке с пометкой "один из". Необязательный терминальный или нетерминальный символ обозначается с помощью нижнего индекса "opt", поэтому { выражение opt } означает необязательное выражение, заключенное в фигурные скобки. R.2 Соглашения о лексических понятиях Программа на С++ состоит из одного или нескольких файлов ($$R.3.3). С логической точки зрения файл транслируется за несколько проходов. Первый проход состоит в препроцессорной обработке ($$R.16), на которой происходит включение файлов и макроподстановка. Работа препроцессора управляется с помощью команд, являющихся строками, первый символ которых отличный от пробела есть # ($$R2.1). Результат работы препроцессора есть последовательность лексем. Такую последовательность лексем, т.е. файл после препроцессорной обработки, называют единицей трансляции. R.2.1 Лексемы Существуют лексемы пяти видов: идентификаторы, служебные слова, литералы, операции и различные разделители. Пробелы, вертикальная и горизонтальная табуляция, конец строки, перевод строки и комментарии (все вместе "обобщенные" пробелы), как указано ниже, игнорируются, за исключением того, что они отделяют лексемы. Обобщенные пробелы нужны, чтобы разделить стоящие рядом идентификаторы, служебные слова и константы. Если входной поток разобран на лексемы до данного символа, то следующей лексемой считается лексема с максимально возможной длиной, которая начинается с этого символа. R.2.2 Комментарии Символы /* начинают комментарий, который завершается символами */. Такие комментарии не могут быть вложенными. Символы // начинают комментарий, который завершается концом этой строки. Символы //, /* и */ не имеют специального назначения в комментарии // и рассматриваются как обычные символы. Аналогично символы // и /* не имеют специального назначения внутри комментария /*. R.2.3 Идентификаторы Идентификатор - это последовательность букв и цифр произвольной длины. Первый символ должен быть буквой, символ подчеркивания _ считается буквой. Прописные и строчные буквы различаются. Все символы существенны. R.2.4 Служебные слова Перечисленные ниже идентификаторы фиксируются как служебные слова и в другом смысле не могут использоваться: asm continue float new signed try auto default for operator sizeof typedef break delete friend private static union case do goto protected struct unsigned catch double if public switch virtual char else inline register template void class enum int return this volatile const extern long short throw while В дополнение к этому идентификаторы, содержащие двойное подчеркивание (__) резервируются для реализаций С++ и стандартных библиотек и пользователи не должны употреблять их. В представлении программы на С++ в кодировке ASCII используются в качестве операций или разделителей следующие символы: ! % ^ & * ( ) - + = { } | ~ [ ] \ ; ' : " < > ? , . / а следующие комбинации символов используются для задания операций: -> ++ -- .* ->* << >> <= >= == != && || *= /= %= += -= <<= >>= &= ^= |= :: Каждая операция считается отдельной лексемой. В дополнении к этому следующие символы резервируются для препроцессора: # ## Определенные, зависящие от реализации, свойства, такие как тип операции sizeof ($$R5.3.2) или диапазоны базовых типов ($$R.3.6.1) определяются в стандартных заголовочных файлах ($$R.16.4) <float.h> <limits.h> <stddef.h> Эти файлы являются частью ANSI стандарта для С. Кроме того заголовочные файлы <new.h> <stdarg.h> <stdlib.h> определяют типы самых важных библиотечных функций. Два последних файла входят в ANSI стандарт для С, файл <new.h> относится только к С++. R.2.5 Литералы Есть несколько видов литералов (часто называемых "константами"). литерал: целая константа символьная константа константа с плавающей точкой строка литералов R.2.5.1 Целые константы Все целые константы, состоящие из последовательности цифр, считаются десятичными (основание счисления десять), если только они не начинаются с 0 (цифра ноль). Последовательность цифр, начинающаяся с 0, считается восьмеричным целым (основание счисления восемь). Цифры 8 и 9 не являются восьмеричными. Последовательность цифр, начинающаяся с 0x или 0X, считается шестнадцатеричным целым (основание счисления шестнадцать). Шестандцатеричные цифры могут состоять из символов от a или A до f или F с десятичными значениями их от десяти до пятнадцати. Например, число двенадцать можно записать как 12, 014 или 0XC. Тип целой константы определяется ее представлением, значением и окончанием. Если она десятичная и не имеет окончания, ее тип будет первым подходящим для ее значения из следующих типов: int, long int, unsigned long int. Если она восьмеричная или шестнадцатеричная и не имеет окончания, ее тип будет первым подходящим для ее значения из следующих: int, unsigned int, long int, unsigned long int. Если она оканчивается символом u или U, ее тип будет первым подходящим для ее значения из следующих: unsigned int, unsigned long int. Если она оканчивается символом l или L, ее тип будет первым подходящим для ее значения из следующих: long int, unsigned long int. Если она оканчивается на ul, lu, uL, Lu, Ul, lU, UL или LU, ее типом будет unsigned long int. R.2.5.2 Символьные константы Символьной константой является один или несколько символов, заключенные в одиночные кавычки, например 'x'. Константа из одного символа имеет тип char. Значение константы из одного символа есть порядковый номер символа в таблице кодировки символов на данной машине. Символьные константы из нескольких символов имеют тип int. Значение такой константы зависит от реализации. Некоторые символы, не имеющие графического представления, как одиночная кавычка ',двойная кавычка ", знак вопроса ?, обратная дробная черта \, можно представлять комбинацией символов (начинающейся с \) в соответствии с приводимой ниже таблицей: конец строки NL (LF) \n горизонтальная табуляция HT \t вертикальная табуляция VT \v шаг назад BS \b возврат каретки CR \r перевод формата (авторегистр) FF \f сигнал BEL \a обратная дробная черта \ \\ знак вопроса ? \? одиночная кавычка ' \' двойная кавычка " \" восьмеричное число ooo \ooo шестнадцатеричное число hhh \xhhh Если за обратной дробной чертой следует символ, отличный от перечисленных, результат неопределен. Комбинация \ooo состоит из обратной дробной черты, а которой следуют одна, две или три восьмеричные цифры. Считается, что они определяют значение искомого символа. Комбинация \xhhh состоит из из обратной дробной черты, за которой следует x, а за ним, в свою очередь, следует последовательность шестнадцатеричных цифр. Считается, что она задает значение искомого символа. Нет ограничения на длину этой последовательности шестнадцатеричных цифр. Последовательность восьмеричных или шестнадцатеричных цифр оканчивается, когда встречается первый символ, который не есть восьмеричная или шестнадцатеричная цифра соответственно. Если значение символьной константы превосходит максимальное из char, то оно определяется реализацией. Символьная константа, которой непосредственно предшествует буква L, является широкой символьной константой, например, L'ab'. Такие константы имеют тип wchar_t, являющийся целочисленным типом ($$R.3.6.1), определенном в стандартном заголовочном файле <stddef.h>. Широкие символы предназначены для такого набора символов, где значение символа не помещается в один байт. R.2.5.3 Константы с плавающей точкой Константы с плавающей точкой состоят из целой части, символа точка, дробной части, e или E, целого показателя с возможным знаком и возможным окончанием, указывающим тип. Целая и дробная части состоят из последовательности десятичных (основание счисления десять) цифр. Или целая часть, или дробная часть (но не обе) могут отсутствовать. Или точка, или символ e (или E) вместе с показателем могут отсутствовать (но не оба). Тип константы с плавающей точкой есть double, если только он не задан явно с помощью окончания. Окончания f или F задают тип float, окончания l или L задают тип long double. R.2.5.4 Строки литералов Строка литералов есть последовательность символов (как они определены в $$R.2.5.2), заключенная в двойные кавычки, т.е. "...". Строка имеет тип "массив символов" и класс памяти static ($$R.3.5), она инициализируется заданными символами. Будут ли все строки различны (т.е. хранится в отдельных объектах), определяется реализацией. Соседние строки литералов конкатенируются. Символы в строках, полученных при конкатенации, хранятся отдельно. Например, после конкатенации "\xA" "B" строка будет содержать два символа '\xA' и 'B' (а не один шестнадцатеричный символ '\xAB'). После всех необходимых конкатенаций к строке добавляется символ '\0', чтобы программа, читающая строку, могла определить ее конец. Размер строки равен числу всех ее символов, включая символ завершитель строки. Внутри строки перед символом двойной кавычки " должен идти символ \. Строка литералов, перед которой непосредственно идет символ L, считается широкосимвольной строкой, например, L"asdf". Такая строка имеет тип "массив элементов типа wchar_t", где wchar_t целочисленный тип, определенный в стандартном заголовочном файле <stddef.h>. Результат конкатенации обычных и широкосимвольных строк литералов неопределен. R.3 Основные понятия Имя обозначает объект, функцию, множество функций, элемент перечисления, тип, член класса, шаблон типа, значение или метку. Имя становится известно в программе с помощью описания. Имя можно использовать только в пределах части программы, называемой областью видимости имени. Имя имеет тип, который задает его использование. Имя, используемое в более чем одной единице трансляции, может обозначать один и тот же (а может и разные) объект, функцию, тип, шаблон типа или значение, в зависимости от компоновки ($$R.3.3) этих единиц трансляции. Объект имеет область для его хранения ($$R.3.7). Поименованный объект имеет класс памяти ($$R.3.5), который определяет его время жизни. Интерпретация значений, хранящихся в объекте, определяется типом выражения, задающего доступ к объекту. R.3.1 Описания и определения Описание ($$r.7) делает известным в программе одно или несколько имен. Описание считается определением, если только оно не описывает функцию без задания ее тела ($$R.8.3), не содержит описателя extern ($$R.7.11), не имеет части инициализации или тела функции, не является описанием статического члена данных в описании класса ($$R.9.4), не является описанием имени класса ($$R.9.1), не является описанием typedef ($$R.7.1.3). Ниже приведены примеры определений: int a; extern const c = 1; int f(int x) { return x+a; } struct S { int a; int b; }; enum { up, down }; тогда как ниже следуют только описания: extern int a; extern const c; int f(int); struct S; typedef int Int; Для каждого объекта, функции, класса и элемента перечисления, используемых в программе, должно быть только одно определение ($$R.3.3). Если функция никогда не вызывается и ее адрес никогда не используется, ее не нужно определять. Аналогично, если имя класса используется только так, что не требуется знать определения класса, то такой класс не надо определять. R.3.2 Область видимости Существует четыре области видимости: локальная, функция, файл и класс. Локальная: Имя, описанное в блоке ($$R.6.3), является локальным в этом блоке и может использоваться только в нем и в блоках, содержащихся в этом блоке и появляющихся после момента описания. Имена формальных параметров рассматриваются, как если бы они были описаны в самом объемлющем блоке этой функции. Функция: Метки ($$R.6.1) можно использовать повсюду в функции, в которой они описаны. Только метки имеют область видимости, совпадающую с функцией. Файл: Имя описанное вне всех блоков ($$R.6.3) и классов ($$R.9) имеет область видимости файл и может быть использовано в единице трансляции, в которой оно появляется после момента описания. Имена, описанные с файловой областью видимости, называются глобальными. Класс: Имя члена класса является локальным в своем классе и оно может быть использовано только в функции-члене этого класса ($$R.9.3), или после операции . , применяемой к объекту данного класса ($$R.5.2.4) или объекту производного класса($$R.10), или после операции ->, применяемой к указателю на объект данного класса ($$R.5.2.4) или на объект производного класса, или после операции разрешения :: ($$R.5.1), примененной к имени данного или производного класса. Имя, введенное с помощью операции friend ($$R.11.4), принадлежит той же области определенности, что и класс, содержащий описание friend. Класс, впервые описанный в операторе return или в типе параметра, принадлежит к глобальной области видимости. Специальные соглашения действуют на имена, введенные при описании параметров функции ($$R.8.2.5) и в описаниях friend ($$R.11.4). Имя может быть скрыто явным описанием того же имени в объемлющем блоке или классе. Скрытое имя члена класса все-таки можно использовать, если оно предваряется именем класса, к которому применена операция :: ($$R.4.1, $$R.9.4, $$R.10). Скрытое имя объекта, функции, типа или элемента перечисления с файловой областью видимости можно использовать, если оно предваряется унарной операцией :: ($$R.5.1). В дополнении к этому, имя класса ($$R.9.1) может быть скрыто именем объекта, функции или элемента перечисления, имеющего ту же область видимости. Если класс и объект, или функция, или элемент перечисления описаны (в любом порядке) с одинаковым именем в одной области видимости, то имя класса становится скрытым. Имя класса, скрытое в локальной области видимости или в области видимости класса именем объекта, функции или элемента перечисления, все-таки можно использовать, если предварить его подходящей спецификацией class, struct или union ($$R.7.1.6). Аналогично, скрытое имя элемента перечисления можно использовать, если предварить его спецификацией типа enum ($$R.7.1.6). В $$R.10.4 приводится сводка правил области видимости. Моментом описания имени считается момент завершения описателя имени ($$R.8), предшествующей части инициализации (если она есть). Например, int x = 12; { int x = x; } Здесь второе x инициализируется своим собственным (неопределенным) значением. Моментом описания элемента перечисления считается момент сразу после появления его идентификатора, например: enum { x = x }; Здесь элемент перечисления x опять инициализируется своим собственным (неопределенным) значением. R.3.3 Программа и связывание Программа состоит из одного или нескольких файлов, связываемых вместе ($$R.2). Файл состоит из последовательности описаний. Имя с файловой областью видимости, которое явно описано как static, является локальным в своей единице трансляции и может использоваться для именования объектов, функций и т.п. в других единицах трансляции. Говорят, что такие имена имеют внутреннее связывание. Имя с файловой областью видимости, которое явно описано со спецификацией inline, является локальным в своей единице трансляции. Имя с файловой областью видимости, которое явно описано со спецификацией const и не описано явно как extern, считается локальным в своей единице трансляции. То же верно для имени класса, которое не использовалось в нелокальных для данной единицы трансляции описаниях объекта, функции или класса, и который не имеет статических членов ($$R.9.4), не имеет функций-членов, кроме подстановок ($$R.9.3.2). Всякое описание некоторого имени с файловой областью видимости, которое не описано одним из перечисленных способов так, чтобы иметь внутреннее связывание, в многофайловой программе обозначает один и тот же объект ($$R.3.7), функцию ($$R.8.2.5) или класс ($$R.9). Такие имена называются внешними или говорят, что они имеют внешнее связывание. В частности, поскольку нельзя описать имя класса как static, всякое употребление имени некоторого класса с файловой областью видимости, который (класс) использовался для описания объекта или функции с внешним связыванием, или же который имеет статический член или функцию-член, не являющуюся подстановкой, будет обозначать один и тот же класс. Имена определяемых типов (typedef $$R.7.13), элементы перечисления ($$R.7.2) или имена шаблонов типа ($$R.14) не имеют внешнего связывания. Статические члены класса ($$R.9.4) допускают внешнее связывание. Функции-члены, не являющиеся подстановкой, допускают внешнее связывание. Функции-члены, являющиеся подстановкой, должны иметь в точности одно определение в программе. Локальные имена ($$R.3.2), явно описанные со спецификацией extern, имеют внешнее связывание, если только они уже не были описаны как static ($$R.7.1.1). Типы, используемые во всех описаниях некоторого внешнего имени, должны совпадать, за исключением использования имен определяемых типов ($$R.7.1.3) и указания границ массивов ($$R.8.2.4). Должно быть в точности одно определение для каждой функции, объекта, класса и элемента перечисления, используемых в программе. Однако, если функция никогда не вызывается и ее адрес никогда не используется, ее не нужно определять. Аналогично, если имя класса используется только таким образом, что не требуется знать определение класса, то класс не нужно определять. Область видимости функции может быть только файл или класс. С помощью спецификации связывания можно добиться связывания с описаниями на другом языке ($$R.7.4). R.3.4 Начало и окончание программы Программа должна содержать функцию с именем main(). Ей приписывается роль начала программы. Эта функция не является предопределенной для транслятора, она не может быть перегружена, а ее тип зависит от реализации. Предполагается, что любая реализация должна допускать два приведенных ниже определения и что можно добавлять после argv любые параметры. Функция main может определяться так int main() { /* ... */ } или int main(int argc, char* argv[]) { /* ... */ } В последнем определении argc задает число параметров, передаваемых программе окружением, в котором она выполняется. Если argc не равно нулю, параметры должны передаваться как строки, завершающиеся символом '\0', с помощью argv[0] до argv[argc-1], причем argv[0] должно быть именем, под которым программа была запущена, или "". Должно гарантироваться, что argv[argc]==0. Функция main() не должна вызываться в программе. Связывание main() ($$R.3.3) зависит от реализации. Нельзя получать адрес main() и не следует описывать ее как inline или static. Вызов функции void exit(int); описанной в <stdlib.h>, завершает программу. Значение параметра передается окружению программы в качестве результата программы. Инициализация нелокальных статических объектов ($$R.3.5) единицы трансляции происходит прежде первого обращения к функции или объекту, определенному в этой единице трансляции. Эта инициализация ($$R.8.4, $$R.9.4, &&R.12.1, $$R.12.6.1) может быть проведена перед выполнением первого оператора main() или отложена до любого момента, предшествующего первому использованию функции или объекта, определенных в данной единице трансляции. Все статические объекты по умолчанию инициализируются нулем ($$R.8.4) прежде любой динамической (во времени выполнения программы) инициализации. Больше никаких требований на порядок инициализации объектов из различных единиц трансляции не налагается. Инициализация локальных и статических объектов описана в $$R.8.4. Деструкторы ($$R.12.4) для инициализированных статических объектов вызываются при возврате из main() или при вызове exit(). Уничтожение происходит в обратном порядке по сравнению с инициализацией. С помощью функции atexit() из <stdlib.h> можно указать функцию, которую нужно вызывать при выходе из программы. Если было обращение к функции atexit(), объекты, инициализированные до вызова atexit(), не должны уничтожаться до тех пор, пока не произойдет вызов функции, указанной в atexit(). Если реализация С++ сосуществует с реализацией С, все действия, которые должны были произойти после вызова функции, заданной в atexit(), происходят только после вызова всех деструкторов. Вызов функции void abort(); описанной в <stdlib.h>, завершает программу без выполнения деструкторов статических объектов и без вызова функций, заданных в atexit(). R.3.5 Классы памяти Существует два описываемых класса памяти: автоматический и статический. Автоматические объекты становятся локальными при передаче управления в каждый блок. Статические объекты существуют и сохраняют свое значение во все время выполнения программы. Автоматические объекты инициализируются ($$R.12.1) всякий раз, когда управление переходит в блок, где они определены и уничтожаются ($$R.12.4) по выходе из этого блока ($$R.6.7). Поименованный автоматический объект не должен быть уничтожен до окончания его блока, точно так же, как не может быть исключен поименованный автоматический объект класса, имеющего конструктор или деструктор с побочным эффектом, даже если кажется, что этот объект не используется. Аналогично, глобальный объект класса с конструктором или деструктором, имеющими побочный эффект, не может быть исключен, даже если кажется, что он не используется. Статические объекты инициализируются и уничтожаются в соответствии с описанием в $$R.3.4 и $$R.6.7. С некоторыми объектами не связано никакого имени, см. $$R.5.3.3 и $$R.12.2. Все глобальные объекты имеют класс памяти статический. Локальным объектам и членам класса можно предать класс памяти статический с помощью явного использования спецификации класса памяти static ($$R.7.1.1). R.3.6 Типы Существуют два вида типов: основные и производные. R.3.6.1 Основные типы Существует несколько основных типов. В стандартном заголовочном файле <limits.h> задаются в зависимости от реализации минимальные и максимальные значения каждого типа. Объекты, описанные как символы (char), могут хранить любой элемент из базового набора символов данной машины. Если символ этого набора хранится в символьной переменной, то ее значение равно целому значению кода этого символа. Символы могут явно описываться как unsigned или signed. Обычный char, signed char и unsigned char являются тремя различными типами. Для всех этих трех типов требуется одинаковый объем памяти. С помощью описаний short int, int и long int можно определить целые трех различных размеров. Для длинных целых требуется памяти не меньше чем для коротких целых, но в реализации или короткие целые, или длинные целые, или и те и другие могут оказаться эквивалентными обычным целым. Обычные целые имеют размер, определяемый системой команд, размеры других целых определяются конкретными потребностями. Для каждого из типов signed char, short, int и long существует соответствующий беззнаковый тип, который занимает тот же объем памяти и удовлетворяет тем же требованиям выравнивания. Требование выравнивание - это ограничение на значение указателя на данный объект, которое накладывает реализация ($$R.5.4). Беззнаковые целые, описанные как unsigned, подчиняются законом арифметики по модулю 2@n, где n число битов, используемое для представления значения. Отсюда следует, что в арифметике беззнаковых не возникает переполнения. Существует три типа с плавающей точкой: float, double и long double. Тип double гарантирует не меньшую точность представления, чем float, а тип long double гарантирует точность не меньше, чем у double. Характеристики основных типов с плавающей точкой определяются в соответствии с реализацией в стандартном заголовочном файле <float.h>. Типы char, int любых размеров и тип перечисления ($$R.7.2) называются целочисленными типами. Целочисленные типы вместе с типами с плавающей точкой образуют арифметические типы. Тип void задает пустое множество значений. Он используется для обозначения типа функций, которые не возвращают результат. Нельзя описывать объекты с типом void. Любое выражение можно явно преобразовать к типу void ($$R.5.4), получившееся выражение можно использовать только как выражение-оператор ($$R.6.2), как левый операнд операции запятая ($$R.5.18) или в качестве второго или третьего операнда в операции ?: ($$R.5.16). R.3.6.2 Производные типы Существует потенциально бесконечное число производных типов, которые строятся из основных типов следующими способами: массив объектов данного типа, $$R.8.4; функции, имеющие параметры данного типа и возвращающие объекты данного типа, $$R.8.2.5; указатели на объекты или функции данного типа, $$R.8.2.1; ссылки на объекты или функции данного типа, $$R.8.2.2; константы, являющиеся значениями данного типа, $$R.7.1.6; классы, содержащие совокупность объектов различных типов ($$R.9), набор функций для управления этими объектами ($$R.9.3) и и список ограничений на доступ к этим объектам и функциям, $$R.11; структуры, которые являются классами без стандартных ограничений на доступ, $$r.11; объединения, которые являются структурами, способными содержать в разное время объекты различных типов, $$R.9.5; указатели на члены классов, которые задают члены данного типа среди всех объектов данного класса, $$R.8.2.3. В общем случае указанные методы построения объектов могут применяться рекурсивно, ограничения приведены в $$r.8.2.1, $$R.8.2.4, $$R.8.2.5 и $$R.8.2.2. Про указатель на объекты типа T говорят "указатель на на T". Например, про указатель на объект типа int говорят "указатель на int", а указатель на объект класса X называется "указатель на X". Объекты типа void* (указатель на void), const void* и volatile void* могут использоваться как указатели на объекты неизвестного типа. Объект типа void* должен иметь достаточно памяти, чтобы хранить указатель на любой объект. Все фрагменты руководства, говорящие об "указателях", не относятся к указателям на члены, за исключением указателей на статические члены. R.3.6.3 Имена типов Основные и производные типы можно поименовать с помощью механизма typedef ($$R.7.1.3), а семейство типов и функций можно задать и поименовать с помощью механизма шаблона типов ($$R.14). R.3.7 Адреса Любой объект - это некоторая область памяти, адрес - выражение, ссылающееся на объект или функцию. Очевидным примером адреса будет имя объекта. Существуют операции, порождающие адреса, например, если E выражение типа указатель, то *E - адресное выражение, соответствующее объекту, на который указывает E. Термин "адрес" ("lvalue" т.е. left value - левая величина) появляется из оператора присваивания E1 = E2, где левый операнд E1 должен "адресовать" изменяемую переменную. При обсуждении всех операций в $$R.5 указывается применимы ли они к адресным операндам и порождают ли они сами адреса. Адрес может изменяться, если он не является именем функции, именем массива или const. R.4 Стандартные преобразования Некоторые операции в зависимости от своих операндов могут вызвать преобразование значения операнда от одного типа к другому. Здесь описываются преобразования, вызванные самыми обычными операциями, и объясняется каковы могут быть результаты таких преобразований. По мере надобности будут даны дополнительные объяснения при обсуждении каждой операции. Подобные преобразования также происходят при инициализации ($$R.8.4, $$R.8.4.3, $$R.12.8, $$R.12.1). В $$R.12.3 и $$R.13.2 описываются преобразования, заданные пользователем, и их соотношения со стандартными преобразованиями. В результате преобразования может получиться адрес, только если результат есть ссылка ($$R.8.2.2). R.4.1 Стандартные преобразования для целочисленных Всюду, где требуется целое, можно использовать char, short int, элемент перечисления ($$R.7.2) или битовое поле ($$R.9.6), причем в знаковом и беззнаковом вариантах. Если int может представлять все значения исходного типа, значение преобразуется к int, иначе оно преобразуется к unsigned int. Это называется стандартным преобразованием для целочисленных. R.4.2 Преобразования целочисленных Если целое преобразуется в беззнаковый тип, то полученное значение есть наименьшее беззнаковое целое, совпадающее с целым со знаком по (mod 2**n), где n есть число битов в представлении беззнакового целого. Для представления в дополнительном коде это преобразование лишь концептуальное, никаких изменений в двоичном представлении в действительности не происходит.\ Если целое преобразуется к знаковому типу, значение не меняется, при условии, что его можно представить с помощью нового типа, иначе значение определяется реализацией. R.4.3 Значения с плавающей точкой и двойной точностью Для выражений типа float может использоваться арифметика с обычной точностью. Если значение с плавающей точкой меньшей точности преобразуется в значение типа float равной или большей точности, то изменения значения не происходит. Если значение с плавающей точкой большей точности преобразуется в значение типа float меньшей точности и значение находится в пределах, задаваемых представлением типа, то в результате может получиться или ближайшее большее или ближайшее меньшее представимое значение. Если результат оказался вне границ представления типа, поведение неопределено. R.4.4 Целочисленные и числа с плавающей точкой Преобразование значения с плавающей точкой к целочисленному типу сводится к "усечению", т.е. отбрасыванию дробной части. Такие преобразования зависят от машины, в частности в какую сторону будет проходить усечение для отрицательных чисел определяется на разных машинах по разному. Результат считается неопределенным, если значение нельзя представить в целочисленном типе. Преобразования целочисленных значений к значениям с плавающей точкой математически корректны настолько, насколько это позволяет система команд. Может произойти потеря точности, если целочисленное значение нельзя точно представить как значение с плавающей точкой. R.4.5 Арифметические преобразования Для большинства операций преобразования операндов и тип результата определяются одними и и теми же правилами. Это правило можно назвать "обычными арифметическими преобразованиями". Если один из операндов есть long double, другой операнд преобразуется в long double. Иначе, если один из операндов есть double, другой операнд преобразуется в double. Иначе, если один из операндов есть float, другой операнд преобразуется в float. Иначе, если стандартные целочисленные преобразования ($$R.4.1) происходят над обоими операндами. Тогда, если один из операндов есть unsigned long, другой операнд преобразуется в unsigned long. Иначе, если один из операндов есть long int, а другой - unsigned int, то при условии, что long int может представлять все возможные значения unsigned int, значение unsigned int преобразуется в long int, в противном случае оба операнда преобразуются в unsigned long int. Иначе, если один из операндов есть long, другой операнд преобразуется в long. Иначе, если один из операндов есть unsigned, другой операнд преобразуется в unsigned. Иначе, оба операнда должны быть int. R.4.6 Преобразования указателей Всюду, где указатели ($$R.8.2.1) присваиваются, инициализируются, сравниваются или используются иным образом, могут происходить следующие преобразования: Константное выражение ($$R.5.19), которое сводится к нулю, преобразуется в указатель, обычно называемый пустым указателем. Гарантируется, что значение такого указателя будет отлично от любого указателя на объект или функцию. Указатель на объект любого типа, не являющегося const или volatile, можно преобразовать в void*. Указатель на функцию можно преобразовать в void*, при условии, что для void* отводится достаточно памяти, чтобы хранить этот указатель. Указатель на данный класс можно преобразовать в указатель на доступный базовый класс данного класса ($$R.10), если такое преобразование не содержит двусмысленность ($$R.10.1). Базовый класс считается доступным, если доступны его общие члены ($$R.11.1). Результатом преобразования будет указатель на объект типа базового класса, вложенный в объект типа производного класса. Пустой указатель (0) преобразуется сам в себя. Выражение типа "массив T" может преобразовываться в указатель на начальный элемент массива. Выражение типа "функция, возвращающая T" преобразуется в "указатель на функцию, возвращающую T", за исключением тех случаев, когда оно используется как операнд адресной операции & или операции вызова функции (). R.4.7 Преобразования ссылок Всюду, где ссылки ($$R.8.2.2) инициализируются (включая передачу параметров ($$R.5.2.2) и возврат значения функции ($$R.6.6.3)) или используются иным образом, возможны следующие преобразования: Ссылка на данный класс может быть преобразована в ссылку на доступный базовый класс ($$R.10, $$R.11.1) данного класса ($$R.8.4.3), при условии, что такое преобразование не содержит двусмысленности ($$R.10.1.1). Результатом преобразования будет ссылка на объект базового класса, вложенный в объект производного класса. R.4.8 Указатели на члены Всюду, где указатели на члены ($$R.8.2.3) инициализируются, присваиваются, сравниваются или используются иным образом, могут происходить следующие преобразования: Константное выражение ($$R.5.19), которое сводится к нулю, преобразуется в указатель на член. Гарантируется, что его значение будет отлично от любых других указателей на члены. Указатель на член данного класса можно преобразовать в указатель на член производного от данного класса, при условии, что допустимо обратное преобразование от указателя на член производного класса в указатель член базового класса, и что оно выполнимо однозначным образом ($$R.10.1.1). Правило преобразования указателей на члены (т.е. от указателя на член базового класса к указателю на член производного класса) выглядит перевернутым, если сравнивать его с правилом для указателей на объекты (т.е. от указателя на производный объект к указателю на базовый объект) ($$R.4.6, $$R.10). Это необходимо для гарантии надежности типов. Отметим, что указатель на член не является указателем на объект или указателем на функцию и правила преобразований таких указателей не применимы для указателей на члены. В частности указатель на член нельзя преобразовать в void*. R.5 Выражения Здесь определяются синтаксис, порядок вычисления и назначение выражений. Выражение - это последовательность операций и операндов, которая задает вычисление. Вычисление может выдавать в качестве результата значение и может вызывать побочные эффекты. Операции могут быть перегружены, т.е. им может быть приписано значение, когда они применяются к выражениям типа класс ($$R.9). Применение перегруженных операций преобразуется в вызовы функций в соответствии с описанием в $$R.13.4. Перегруженные операции подчиняются синтаксическим правилам, определенным в этом разделе, но требования к типу операнда, адресу и порядку вычисления заменяются на правила вызова функции. Соотношения между операциями, типа ++a означает a+=1, не гарантируются для перегруженных операций ($$R.13.4). В этом разделе описано применение операций к типам, для которых они не являются перегруженными. Перегрузка операций не может изменить правила применения операций к типам, для которых такое применение предусмотрено в самом языке. Порядок вычисления подвыражений определяется приоритетом и порядком применения операций. Обычные математические правила ассоциативности и коммутативности операций действуют только, если операции действительно ассоциативны или коммутативны. За исключением оговоренных случаев порядок вычисления операндов конкретной операции неопределен. В частности, если в выражении значение изменяется дважды, результат выражения неопределен, если только порядок выполнения не обеспечивается самими операциями, например: i = v[i++]; // the value of `i' is undefined i=7,i++,i++; // `i' becomes 9 Реакция на переполнение и деление на нуль при вычислении выражения зависит от реализации. В большинстве существующих реализаций С++ игнорируется переполнение целых. Реакция на деление на нуль и ошибки операций над числами с плавающей точкой варьируется от машины к машине и обычно связана с соответствующими библиотечными функциями. Кроме оговоренных случаев, операнды типа const T, volatile T, T&, const T& и volatile T& можно использовать, как если бы они имели тип просто T. Аналогично, операнды типа T* const, T*volatile можно использовать, как если бы они имели тип просто T*, за исключением оговоренных случаев. Аналогично, просто тип T можно использовать всюду, где требуется тип volatile T или const T. Эти правила может применять в комбинации, так что const T* volatile можно использовать там, где требуется T*, за исключением оговоренных случаев. При рассмотрении разрешения перегрузки ($$R.13.2) такое использование операций не считается стандартным преобразованием операндов. Если выражение имеет тип "ссылка на T" ($$R.8.2.2, $$R.8.4.3), значение выражение есть объект типа "T", на который настроена ссылка. Выражение является адресом. Ссылку можно представлять как имя объекта. Допустимы определенные пользователем преобразования объектов класса в (и обратно) основные типы, указатели и т.д. ($$R.12.3) Если они недвусмысленны ($$R.13.2), такие преобразования могут применяться транслятором всегда, когда появляется объект типа класса в качестве операнда операции, в качестве инициализирующего выражения ($$R.8.4), в качестве выражения, задающего условие ($$R.6.4), или в качестве выражения, используемого в операторе цикла ($$R.6.5), или в качестве значения, возвращаемого функцией ($$R.6.6.3), или в качестве параметра функции ($$R.5.2.2). R.5.1 Первичные выражения Первичными выражениями являются литералы, имена и имена, определенные с помощью операции разрешения области видимости ::. первичное-выражение: литерал this :: идентификатор :: имя-функции-операции :: уточненное-имя ( выражение ) имя Литерал является первичным выражением. Его тип определяется его видом ($$R.2.5). В теле нестатической функции-члене ($$R.9.3) служебное слово this обозначает указатель на объект, к которому относится вызов функции. Служебное слово this нельзя использовать вне тела функции-члена класса. Операция :: , за которой следует идентификатор или имя-операции-функции или уточненное-имя являются первичным выражением. Его тип задается описанием идентификатора, имени или имени-функции-операции. Результатом является идентификатор, имя или имя-функции-операции. Результат является адресом, если идентификатор является адресом. Идентификатор или имя-функции-операции должны иметь файловую область видимости. С помощью операции :: можно обращаться к типу, объекту, функции или элементу перечисления, даже если обозначающий их идентификатор является скрытым ($$R.3.2). Выражение в скобках является первичным выражением, тип и значение которого идентичны им же у выражения без скобок. Наличие скобок не влияет на то, является выражение адресом или нет. Понятие имя - это определенное первичное-выражение, которое может появляться только после . и -> ($$R.5.2.4): имя: идентификатор имя-функции-операции имя-функции-преобразования ~имя-класса уточненное-имя Идентификатор есть имя, при условии что он описан надлежащим образом ($$R.7). Понятие имя-функции-операции описано в ($$R.13.4), а понятие имя-функции-преобразования в ($$R.12.3.2). Конструкция ~имя-класса обозначает деструктор ($$R.12.4). уточненное-имя: уточняющее-имя-класса :: имя Понятие уточняющее-имя-класса, за которым следует :: и имя члена этого класса ($$R.9.2), или члена базового по отношению к данному класса ($$R.10) является уточненное-имя. Его тип есть тип члена, а результат выражения есть этот член. Результат является адресом, если таковым является член. Имя класса может быть скрыто другим именем (не типа), в таком случае все равно имя класса доступно и его можно использовать. Если используется имя-класса::имя-класса или имя-класса::~имя-класса, оба понятия имя-класса должны обозначать один и тот же класс. С помощью такой записи обозначаются конструкторы ($$R.12.1) и деструкторы ($$R.12.4) соответственно. Можно использовать уточняющие имена неоднократно, например, N1::N2::N3::n, чтобы обозначать вложенные типы ($$R.9.7). R.5.2 Постфиксные выражения Постфиксные выражения применяются слева направо. постфиксное-выражение: первичное-выражение постфиксное-выражение [ выражение ] постфиксное-выражение ( список-выражений opt ) имя-простого-типа ( список-выражений opt ) постфиксное-выражение . имя постфиксное-выражение -> имя постфиксное-выражение ++ постфиксное-выражение -- список-выражений: выражение-присваивания список-выражений , выражение-присваивания R.5.2.1 Индексация Постфиксное выражение, за которым следует выражение в квадратных скобках, является постфиксным выражением. Интуитивный смысл его индексирование. Первое из выражений должно иметь тип "указатель на T", а второе быть целочисленного типа. Тип результата есть "T". Выражение E1[E2] совпадает (по определению) с выражением *((E1) + (E2)). Подробности операций * и + даны в $$R.5.3 и $$R.5.7, а массивы обсуждаются в $$R.8.2.4. R.5.2.2 Вызов функции Вызов функции является постфиксным выражением, за которым следует, возможно пустой, список выражений в скобках, разделенных запятой. Эти выражения образуют фактические параметры функции. Постфиксное выражение должно иметь тип "функция, возвращающая T", "указатель на функцию, возвращающую T" или "ссылка на функцию, возвращающую T", а результат операции вызова имеет тип "T". При вызове функции происходит инициализация каждого формального параметра ($$R.8.4.3, $$R.12.8, $$r.12.1) фактическим параметром. Производятся стандартные ($$R.4) и заданные пользователем ($$R.12.3) преобразования типа. В функции может изменяться значения непостоянных формальных параметров, но эти изменения не могут повлиять на значения фактических параметров, кроме того случая, когда формальный параметр имеет тип ссылки без спецификации const ($$R.8.2.2). Если формальный параметр имеет тип ссылки при необходимости может создаваться временная переменная ($$R.7.1.6, $$R.2.5,$$R.2.5.4,$$R.8.2.4, $$R.12.2). Добавим, что возможно изменение непостоянных объектов с помощью параметров-указателей. Функцию можно описать таким образом, что она сможет использовать меньшее число параметров (определив параметры по умолчанию $$R.8.2.6) или большее число параметров (с помощью эллипсиса ... $$R.8.2.5), чем было указано при определении функции ($$R.8.3). Функцию можно вызвать только, если описание ее доступно в той области видимости, где происходит вызов. Отсюда следует, всякий формальный параметр, соответствующий некоторому фактическому параметру, должен быть доступен, если не считать эллипсис (...). Перед вызовом всякий фактический параметр типа float, для которого нет формального параметра, преобразуется к типу double, а типа char, short, перечисления или битовое поле, для которого нет формального параметра, преобразуется к типу int или unsigned согласно стандартным преобразованиям целочисленных ($$R.4.1). Объект, являющийся классом и не имеющий описания формального параметра, передается при вызове как структура данных. Объект, являющийся классом и имеющий описание формального параметра передается с помощью инициализации формального параметра фактическим параметром, которая происходит перед выполнением функции посредством вызова конструктора ($$R.12.2, $$R.12.8). Порядок вычислений параметров неопределен и учтите, что он может быть различен у разных трансляторов. Все побочные эффекты выражений фактических параметров могут происходить перед началом выполнения функции. Порядок вычисления постфиксных выражений и списка выражений параметров неопределен. Допустимы рекурсивные вызовы. Операция вызова функции порождает адрес только, если тип результата есть адрес. R.5.2.3 Явные преобразования типа Конструкция имя-простого-типа ($$R.7.1.6), за которой следует список-выражений в скобках образует значение указанного типа с учетом списка выражений. Если список выражений содержит более одного значения, тип должен быть классом с конструктором, описанным соответствующим образом ($$R.8.4, $$R.12.1). Конструкция имя-простого-типа ($$R.7.1.6), за которой следует пара скобок (пустая), образует значение указанного типа. Если тип является классом с конструктором, описанным соответствующим образом, будет вызван этот конструктор, в противном случае результатом будет неопределенное значение указанного типа, см. так же ($$R.5.4). R.5.2.4 Доступ к члену класса Постфиксное выражение, за которым следует точка (.) и имя, является постфиксным выражением. Первое выражение должно быть объектом типа класс, а имя должно быть именем члена этого класса. Результатом будет поименованный член объекта и он будет адресом, если член является адресом. Постфиксное выражение, за которым следует стрелка (->) и имя, является постфиксным выражением. Первое выражение должно быть указателем на объект типа класс, а имя должно быть именем члена этого класса. Результатом будет поименованный член объекта, на который настроен указатель и он будет адресом, если член является адресом. Значит выражение E1->MOS тоже самое, что (*E1).MOS. Обратите внимание, что "объекты типа класс" могут быть структурами ($$R.9.2) или объединениями ($$R.9.5). Классы обсуждаются в $$R.9. R.5.2.5 Инкремент и декремент Значение, получаемое в результате применения постфиксной операции ++, есть значение операнда. Операнд должен быть изменяемым адресом. Тип операнда должен быть арифметический или тип указателя. После выборки результата (для дальнейшего использования) объект увеличивается на 1. Тип результата совпадает с типом операнда, но не является адресом (см. так же $$R.5.7 и $$R.5.17). Постфиксная операция -- сводится к операции декремента (уменьшение на 1) и аналогична операции ++. R.5.3 Унарные операции Выражения с унарными операциями выполняются справа налево. унарное-выражение: постфиксное-выражение ++ унарное выражение -- унарное выражение унарная-операция выражение-приведения sizeof унарная-операция sizeof ( имя-типа ) выражение-размещения выражение-освобождения унарная-операция: один из * & + - ! ~ Унарная операция * означает косвенность: выражение должно быть указателем, а результат является адресом, ссылающимся на объект, на который указывает выражение. Если тип выражения есть "указатель на T", то тип результата будет "T". Результатом унарной операции & будет указатель на ее операнд. Операнд должен быть функцией или адресом или конструкцией уточненное-имя. Для первых двух случаев, если тип выражения есть "T", то тип результата будет "указатель на T". В частности, адрес объекта типа const T имеет тип const T*, тоже верно для volatile. Для случая уточненное имя если член класса "C" не является статическим и имеет тип "T", то тип результата операции будет "указатель на член C типа T". Для статических членов типа T результатом будет обычный "указатель на T". Адрес перегруженной функции ($$R.13) можно брать только при инициализации или присваивании, в котором левая часть однозначно определяет какая версия перегруженной функции имеется ввиду ($R13.3). Операнд унарной операции + должен быть арифметического типа или типа указатель и результатом будет значение операнда. Для целочисленных операндов производится стандартное преобразование целочисленных. Тип результата есть тип преобразованного операнда. Операнд унарной операции - должен иметь арифметический тип и результатом будет изменение знака операнда. Для целочисленных операндов выполняется стандартное преобразование целочисленных. Операция для беззнаковых величин выполняется с помощью вычитания значения операнда из 2**n, где n число битов в представлении преобразованного операнда. Тип результата есть преобразованного операнда. Операнд операции логического отрицания ! должен иметь арифметический тип или быть указателем, результат равен 1, если значение операнда есть 0, и равен 0, если операнд не равен 0. Тип результата есть int. Операнд операции ~ должен иметь целочисленный тип, результатом будет обращение двоичного представления операнда. Выполняются стандартные преобразования целочисленных. Тип результата есть тип преобразованного операнда. R.5.3.1 Инкремент и декремент Операнд префиксной операции ++ увеличивается на 1. Операнд должен быть изменяемым адресом. Тип операнда должен быть арифметическим или указателем. Результатом является новое значение операнда, оно считается адресом. Выражение ++x эквивалентно x+=1. Для уточнения преобразований можно обратиться к описанию сложения ($$R.5.7) и операций присваивания ($$R.5.17). Префиксная операция -- сводится к уменьшению на 1 и выполняется аналогично префиксной операции ++. R.5.3.2 Операция sizeof Операция sizeof вычисляет размер своего операнда в байтах. Операнд должен быть или выражением, которое не вычисляется, или именем типа в скобках. Операцию sizeof нельзя применять к функции, битовому полю, неопределенному классу, типу void или к массиву с неуказанными границами индексов. Байт никак не определяется языком, кроме как результата операции sizeof, именно sizeof(char) есть 1. Если операция применяется к ссылке, результатом будет размер объекта, на который настроена ссылка. Если она применяется к классу, результатом будет размер объекта этого класса в байтах с учетом всех дополнительных байтов, которые потребуется для размещения такого объекта в массиве. Размер любого класса или объекта класса больше нуля. В случае массива операция выдает полное число байтов в массиве. Отсюда следует, что размер массива из n элементов равен размеру элемента, умноженному на n. Операция sizeof может применяться к указателю на функцию, но не к самой функции. Результатом операции будет константа типа size_t. Этот тип определен в стандартном заголовочном файле <stddef.h> и является зависящим от реализации беззнаковым целочисленным типом. R.5.3.3 Операция new Операция new предназначена для создания объекта типа имя-типа ($$R.8.1). Этот тип должен быть типом объекта и функции нельзя размещать с ее помощью, хотя указатели на функции можно. выражение-размещения: ::opt new параметры-new opt имя-типа-new инициализатор-new ::opt new параметры-new opt ( имя-типа ) инициализатор-new параметры-new: ( список-выражений ) имя-типа-new: список-спецификаций-типа описатель-new opt описатель-new: * список-спецификаций-cv opt описатель-new opt имя-класса :: список-спецификаций-cv opt описатель-new opt описатель-new opt [ выражение ] инициализатор-new: ( список-инициализаторов opt ) Время жизни объекта, созданного с помощью new, не ограничивается областью видимости, в которой он был создан. Операция new возвращает указатель на созданный объект. Если объект является массивом, возвращается указатель на начальный элемент массива. Например, обе операции new int и new int[1] возвратят int* , а типом new int[i][10] будет int(*)[10]. Если описывается тип массива ($$R.8.2.4), все размерности, кроме первой, должны быть выражениями- константами ($$R.5.19) с положительным значением. Первая размерность массива может задаваться произвольным выражением, даже если используется имя-типа (здесь нарушается общее требование, чтобы размерности массива в конструкции имя-типа были выражениями-константами ($$R.5.19)). Допускается, чтобы вызывалась функция operator new() с параметром нуль. В таком случае возвращается указатель на объект. При повторении таких вызовов будут возвращаться указатели на разные объекты. Конструкция список-спецификаций-типа не должна содержать const, volatile, описание класса или перечисления. Для резервирования памяти операция new обращается к функции operator new() ($$R.12.5). При размещении объекта типа T ей в качестве первого параметра передается sizeof(T). Конструкция параметры-new используется для передачи дополнительных параметров. Например, операция new T приводит к вызову operator new(sizeof(T)), а операция new(2,f) T приводит к вызову operator new(sizeof(T),2,f). Конструкция параметры-new может использоваться только, если описана функция operator new() с параметрами соответствующих типов. Если с помощью операции new создается объект не типа класс (в том числе и массив объектов типа класс), то вызывается глобальная функция ::operator new(). Если с помощью new создается объект класса T, вызывается функция T::operator new(), если она существует (используя обычные правила просмотра при поиске членов класса и его базовых классов, $$R.10.1.1), иначе вызывается глобальная функция ::operator new(). Использование операции ::new() гарантирует, что будет вызываться глобальная функция ::operator new(), даже если существует T::operator new(). Конструкция выражение-размещения может содержать инициализатор-new. Для объектов классов с конструкторами ($$R.12.1) задаваемый ею список параметров будет использоваться при вызове конструктора, в других случаях конструкция инициализатор-new должна иметь вид ( выражение ) или ( ). Если выражение присутствует, оно используется для инициализации объекта, если его нет, объект начнет существование с неопределенным значением.\ Если класс имеет конструктор, объект этого класса можно создать с помощью new только при условии, что заданы подходящие параметры, или, что класс имеет стандартный конструктор ($$R.12.1). Отводит ли память при создании объекта типа класс сама функция operator new, или оставляет это на конструктор, зависит от реализации. Как для конструктора, так и для функции operator new() проводится проверка возможности доступа и однозначности ($$R.12). Для массивов нельзя задавать инициализаторы. Массивы объектов типа класса с конструктором можно создавать с помощью операции new только, если конструктор класса является стандартным ($$R.12.1). В этом случае стандартный конструктор будет вызываться для каждого элемента массива. Инициализация производится только в том случае, когда функция operator new() возвращает ненуль. Если она возвращает 0 (пустой указатель), значение выражения есть 0. Порядок вычисления выражения вызова operator new() для получения памяти и порядок вычисления параметров конструктора неопределен. Так же неопределено вычисляются ли параметры конструктора, если функция operator new() возвратила 0. В конструкции имя-типа-new скобки использовать необязательно. Тогда обращение new int (*[10])(); // error может привести к ошибке, т.к. операции применяются в таком порядке (new int) (*[10])(); // error Объекты сложного типа можно задать в операции new с помощью явно указанных скобок, например, обращение new (int (*[10])()); размещает массив из 10 указателей на функции (не имеющие параметров и возвращающие int). Конструкции имя-типа-new в выражение-размещения должна быть самой длинной из возможных последовательностей конструкций описатель-new. Это предотвращает коллизии между операциями из описателей &, *, [] и их двойниками из выражения, например, new int* i; // syntax error: parsed as `(new int*) i' // not s `(new int)*i' Символ * используется в описателе указателя, а не в качестве операции умножения. R.5.3.4 Операция delete Операция delete уничтожает объект, созданный с помощью new. выражение-освобождения: ::opt delete выражение-приведения ::opt delete [] выражение-приведения Результат имеет тип void. Операндом delete должен быть указатель, который возвращает new. Эффект применения операции delete к указателю, который не получен в результате операции new без задания параметры-new, считается неопределенным и обычно приводит к опасным последствиям. Однако гарантируется, что удаление по указателю с нулевым значением безопасно. Результат попытки доступа к удаленному объекту неопределен, а удаление объекта может изменить его значение. Более того, если выражение, задающее объект, является изменяемым адресом, его значение после удаления неопределено. Нельзя удалять указатель на константу. Операция delete вызывает деструктор (если он есть $$12.4) для объекта, на который настроен ее операнд. Для освобождения памяти, отведенной под указываемый объект, операция delete вызывает функцию operator delete ($$R.12.5). Для объектов, не имеющих тип класс (в том числе и для массивов классов), используется глобальная функция ::operator delete(). Для объекта типа класс T вызывается функция T::operator delete(), если она есть (используя обычные правила просмотра при поиске членов класса и производных от него классов, $$R.10.1.1), в противном случае вызывается глобальная функция ::operator delete(). Обращение ::delete гарантирует, что будет вызываться глобальная функция ::operator delete(), даже если существует T::operator delete(). Для удаления массивов используется обращение вида delete [ ] выражение-приведения Здесь выражение должно указывать на массив. Если есть деструкторы, они будут вызываться для удаления указанных объектов. Результат удаления массива с помощью простого обращения delete неопределен, так же как и удаление одиночного объекта с помощью delete []. R.5.4 Явное преобразование типа Явное преобразование типа можно задать с помощью функциональной записи ($$R.5.2.3) или с помощью операции приведения. выражение-приведения: унарное-выражение ( имя-типа ) выражение-приведения Задание с помощью операции приведения используется для обозначения преобразования к типу, который не является конструкцией имя-простого-типа. В операции приведения нельзя определять типы. Всякое преобразование типа, не упомянутое здесь и не являющееся преобразованием явно определенным пользователем ($$R.12.3), считается ошибкой. Любой тип, который можно преобразовать в другой с помощью стандартного преобразования ($$R.4), можно также преобразовать с помощью явного преобразования (приведения) и смысл преобразования будет тот же. Указатель можно преобразовать к любому целочисленному типу, достаточно большому, чтобы вместить значение указателя. Алгоритм преобразования зависит от реализации, но предполагается, что он будет естественным для того, кто знает систему адресации, используемой машины. Значение целочисленного типа может быть явно преобразовано в указатель. Указатель, преобразованный в целое достаточного размера (если такие есть в реализации), и преобразованный обратно к типу указателя, должен иметь свое первоначальное значение. Все другие детали перевода указателя в целое и обратно зависят от реализации. Указатель на объект одного типа может быть преобразован в указатель на объект другого типа (с соблюдением ограничений, указанных здесь). Использование получившегося указателя может вызвать особую адресную ситуацию ("неверный адрес"), если преобразуемый указатель не настроен на объект, правильным образом выравненный в памяти. Гарантируется, что указатель на объект данного размера можно преобразовать в указатель на объект равного или меньшего размера и провести обратное преобразование без изменения значения указателя. На различных машинах двоичное представление указателей может быть различно как и требования на выравнивания объектов. Составные объекты выравниваются по самой строгой границе, требуемой их составляющими. Указатель типа void* считается совместимым с указателем на объект любого типа. Указатель на класс B можно преобразовать в указатель на класс D, для которого класс B является прямо или опосредованно базовым классом, если существует однозначное преобразование из D в B ($$R.4.6, $$.R10.1.1) и если B является виртуальным базовым классом ($$R.10.1). Такое приведение от базового класса к производному классу предполагает, что объект базового класса является вложенным по отношению к объекту производного класса. В результате получится указатель, настроенный на объемлющий объект производного класса. Если объект базового класса не содержится ни в каком объекте производного класса, такая операция приведения может вызвать особую ситуацию. Пустой указатель (0) преобразуется сам в себя. Пока еще неопределенный класс можно использовать в операции приведения указателя, в этом случае никаких допущений о структуре класса не делается ($$R.10.1). Любой объект можно явно преобразовать к типу ссылки X&, если указатель на этот объект можно явно преобразовать в тип X*. В результате приведения к ссылке не происходит вызовов конструкторов или функций преобразований. Преобразование ссылки на базовый класс в ссылку на производный класс рассматривается аналогично преобразованию указателя на базовый класс в указатель на производный класс, учитывая вопросы однозначности, виртуальных классов и т.д. Результатом приведения к ссылке является адрес, в отличие от всех остальных приведений. Результат приведения указателя или ссылки настроен на тот же объект, что и исходное выражение без операции приведения. Указатель на функцию можно явно преобразовать в указатель на некоторый объект при условии, что тип указателя на этот объект достаточно велик, чтобы хранить указатель на функцию. Указатель на некоторый объект можно явно преобразовать в указатель на функцию при условии, что тип указателя на функцию достаточно велик, чтобы хранить указатель на этот объект. В обоих случаях, использование указателя, получившегося в результате преобразования, может вызвать особую адресную ситуацию, или что-нибудь похуже, если исходный указатель не настроен на соответствующий объект. Указатель на функцию одного типа можно явно преобразовать в указатель на функцию другого типа. Результат вызова функции с помощью указателя на функцию, тип которой отличен от типа, использованного при определении первой функции, неопределен (см. так же $$R.4.6). Объект или значение можно преобразовать в объект типа класс только при условии, что определен подходящий конструктор или операция преобразования ($$R.12.3). Указатель на член можно явно преобразовать в указатель на другой член, если оба участвующих типа являются типами указателей на члены одного класса, или, если оба типа являются указателями на функцию-член классов, один из которых получается как однозначное производное от другого ($$R.4.8). Указатель на объект с типом, имеющим спецификацию const, можно привести к указателю с типом без спецификации const. Получившийся в результате указатель будет настроен на исходный объект. Объект с типом, имеющим спецификацию const, или ссылку на объект такого типа можно привести в ссылку на объект с типом без const. Получившаяся в результате ссылка будет настроена на исходный объект. В результате попытки изменить этот объект с помощью такой ссылки или указателя может возникнуть особая ситуация или он будет таким же, как при обращении с помощью исходной ссылки или указателя к объекту, тип которого не содержит const. Возникнет ли особая адресная ситуация зависит от реализации. Указатель на объект типа со спецификацией volatile можно привести к указателю на объект типа без volatile. В результате получится указатель, настроенный на исходный объект. Объект типа с volatile или ссылку на такой объект можно привести к ссылке на объект с типом без volatile. R.5.5 Операции указатель-на-член Операции указатель-на-член применяются слева направо. выражение-pm: выражение-приведения выражение-pm .* выражение-приведения выражение-pm ->* выражение-приведения Бинарная операция .* связывает свой второй операнд, который должен иметь тип "указатель на член класса T", с первым операндом, имеющим тип класс T или такой класс, для которого T является однозначно определенным и достижимым базовым классом. Результатом будет объект или функция с типом, задаваемым вторым операндом. Бинарная операция ->* связывает свой второй операнд, который должен иметь тип "указатель на член класса T", с первым операндом, имеющим тип "указатель на T" или тип "указатель на класс, для которого T является однозначно определенным и достижимым базовым классом". Результатом будет объект или функция с типом, задаваемым вторым операндом. Если результат .* или ->* есть функция, то его можно использовать только в качестве операнда операции вызова функции (), например, операция (ptr_to_obj->*ptr_to_mfct)(10); приводит к вызову функции-члена, обозначенной ptr_to_mfct, для объекта, на который настроен указатель ptr_to_obj. Результат операции .* или ->* является адресом, если второй операнд есть адрес. R.5.6 Мультипликативные операции Мультипликативные операции *, /, и % выполняются слева направо. Мультипликативное-выражение: выражение-pm мультипликативное-выражение * выражение-pm мультипликативное-выражение / выражение-pm мультипликативное-выражение % выражение-pm Операнды операций * и / должны иметь арифметический тип, операнды для % должны быть целочисленного типа. Обычные арифметические преобразования ($$R.4.5) производятся над операндами и определяют тип результата. Бинарная операция * обозначает умножение. Бинарная операция / вычисляет частное, а бинарная операция % вычисляет остаток от деления первого выражения на второе. Если второй операнд у / или % есть 0, результат неопределен, иначе (a/b)*b + a%b должно равняться a. Если оба операнда неотрицательны, то таким же будет и результат, в противном случае знак результата определяется реализацией. R.5.7 Аддитивные операции Аддитивные операции + и - выполняются слева направо, при этом происходят обычные арифметические преобразования ($$R.4.5) операндов арифметического типа. аддитивное-выражение: мультипликативное-выражение аддитивное выражение + мультипликативное-выражение аддитивное-выражение - мультипликативное-выражение Операнды должны быть арифметического типа или типа указателя. Результатом операции + является сумма операндов. Можно складывать указатель на объект в массиве и значение любого целочисленного типа. Результатом будет указатель того же типа, что и исходный указатель, но он будет настроен на другой объект массива с заданным смещением от исходного объекта. Так, если P есть указатель на объект массива, выражение P+1 является указателем на следующий объект массива. Если же получившийся в результате сложения указатель вышел за границы массива, результат будет неопределенным, кроме случая, когда указатель настроен на первый адрес больший верхней границы массива. Результатом операции - будет разность операндов. Значение любого целочисленного типа можно вычитать из указателя, при этом применяются те же преобразования, что и для операции +. Никакие другие сочетания типов для указателей не допустимы. Если вычитаются два указателя на объекты одного типа, результатом будет целочисленное значение со знаком, которое показывает на сколько объектов этого типа отстоят друг от друга указуемые объекты. Указатели на соседние элементы массива отстоят на 1. Тип результата зависит от реализации, но он должен быть определен как ptrdiff_t в стандартном заголовочном файле <stddef.h>. Результат не определен, если указатели не настроены на элементы одного массива. Если P есть указатель на последний элемент массива, то (P+1) - 1 есть P. R.5.8 Операции сдвига Операции сдвигов << и >> выполняются слева направо. сдвиговое-выражение: аддитивное-выражение сдвиговое-выражение << аддитивное выражение сдвиговое-выражение >> аддитивное выражение Операнды должны быть целочисленного типа, и над ними производятся стандартные целочисленные преобразования. Тип результата совпадает с типом преобразованного левого операнда. Результат не определен, если правый операнд отрицателен или больше или равен числу разрядов в двоичном представлении преобразованного левого операнда. Значением выражения E1<<E2 будет E1 (рассматриваемое как набор разрядов), сдвинутое влево на E2 разрядов, причем освободившиеся разряды заполняются нулями. Значением выражения E1>>E2 будет E1, сдвинутое вправо на E2 разрядов. Если E1 беззнакового типа или имеет неотрицательное значение, гарантируется, что сдвиг вправо - логический (заполнение нулями), иначе результат зависит от реализации. R.5.9 Операции отношения Операции отношения выполняются слева направо, но этот факт мало что дает, ибо выражение a<b<c означает (a<b)<c, а вовсе не (a<b)&&(b<c). выражение-отношения: сдвиговое-выражение выражение-отношения < сдвиговое-выражение выражение-отношения > сдвиговое-выражение выражение-отношения <= сдвиговое-выражение выражение-отношения >= сдвиговое-выражение Операнды должны быть арифметического типа или типа указателей. Операции < (меньше чем), > (больше чем), <= (меньше или равно) и >= (больше или равно) дают результат 0, если указанное отношение не выполняется, и 1, если оно выполняется. Тип результата int. Над арифметическими операндами выполняются обычные арифметические преобразования. Над указателями выполняются обычные преобразования указателей. Предполагается, что любой указатель можно сравнить с выражением, имеющим результат 0, и любой указатель можно сравнить с указателем, имеющим тип void* (в этом случае указатель сначала преобразуется к типу void*). Указатели на объекты или функции одного типа (после преобразования указателей) можно сравнивать, результат зависит от взаимного расположения в памяти объектов или функций. Два указателя на один и тот же объект считаются равными. Если два указателя настроены на нестатические члены одного объекта, то указатель, настроенный на член, описанный позднее, считается большим, при условии, что члены не имеют разных спецификаций указатель-доступа ($$R.11.1), а класс не является объединением. Если два указателя настроены на нестатические члены одного объекта и спецификации указателей-доступа ($$R.11.1) этих членов различны, результат будет не определен. Если два указателя настроены на члены (данные) одного и того же объединения, они считаются равными. Если два указателя настроены на элементы одного массива или смотрят за границу массива, то указатель, настроенный на элемент с большим индексом, будет большим. Все другие сравнения указателей определяются реализацией. R.5.10 Операции сравнения на равенство выражение-равенства: выражение-отношения выражение-равенства == выражение-отношения выражение-равенства != выражение-отношения Операции == (равно) и != (не равно) аналогичны операциям отношения, за исключением того, что их приоритет ниже. (Таким образом, операция a<b == c<d дает результат 1, если выражения a<b и c<d имеют одно и то же значение.) Кроме этого, можно сравнивать указатели на члены одного типа. Производятся преобразования указателя на член ($$R.4.8). Указатель на член можно сравнить с выражением-константой, которое дает результат 0. R.5.11 Поразрядная операция И выражение-И: выражение-равенства выражение-И & выражение-равенства Выполняются обычные арифметические преобразования, результат - поразрядная функция И от операндов. Операция применима только к целочисленным операндам. R.5.12 Поразрядная (исключающая) операция ИЛИ выражение-исключающего-ИЛИ: выражение-И выражение-исключающего-ИЛИ ^ выражение-И Выполняются обычные арифметические преобразования, результат - поразрядная исключающая функция ИЛИ от операндов. Операция применима только к целочисленным операндам. R.5.13 Поразрядная (включающая) операция ИЛИ выражение-ИЛИ: выражение-исключающего-ИЛИ выражение-ИЛИ | выражение-исключающего-ИЛИ Выполняются обычные арифметические преобразования, результат - поразрядная функция ИЛИ от операндов. Операция применима только к целочисленным типам. R.5.14 Логическая операция И логическое-выражение-И: выражение-ИЛИ логическое-выражение-И && выражение-ИЛИ Операции && выполняются слева направо. Такая операция дает результат 1, если оба операнда ее отличны от нуля, иначе результат - 0. В отличие от & при операции && гарантируется вычисление слева направо, более того, второй операнд не вычисляется, если первый операнд равен 0. Операнды не обязательно имеют одинаковый тип, но каждый должен быть арифметического типа или типа указателя. Тип результата int. Все побочные эффекты вычисления первого выражения могут возникать до вычисления второго выражения. R.5.15 Логическая операция ИЛИ логическое-выражение-ИЛИ: логическое-выражение-И логическое-выражение-ИЛИ || логическое-выражение-И Операции || выполняются слева направо. Результат операции 1, если один из ее операндов отличен от нуля, иначе результат - 0. В отличие от | при операции || гарантируется вычисление слева направо, более того, второй операнд не вычисляется, если значение первого операнда отлично от нуля. Операнды не обязательно имеют одинаковый тип, но каждый должен быть арифметического типа или типа указателя. Тип результата int. Все побочные эффекты вычисления первого выражения могут возникать до вычисления второго выражения. R.5.16 Операция условия выражение-условия: логическое-выражение-ИЛИ логическое-выражение-ИЛИ ? выражение : выражение-условия Условные выражения выполняются слева направо. Первое выражение должно быть арифметического типа или типа указателя. Оно вычисляется, и, если результат его отличен от нуля, то результатом условного выражения будет значение второго выражения, иначе результат - значение третьего выражения. Все побочные эффекты вычисления первого выражения могут возникать до вычисления второго или третьего выражения. Если второе и третье выражение арифметического типа, и типы их совпадают, то таким же будет и тип результата, если они различаются, то выполняются обычные арифметические преобразования, чтобы привести их к общему типу. Если второе и третье выражение являются указателями или выражением-константой, дающим результат 0, выполняются преобразования указателей, чтобы привести результаты выражений к общему типу. Если второе и третье выражение являются ссылками, выполняется преобразование ссылок, чтобы привести их к общему типу. Если второе и третье выражение имеют тип void, общий тип будет void. Если второе и третье выражение имеют один тип класс T, общим типом будет T. Иначе, выражение считается недопустимым. Тип результата есть общий тип. Вычисляется только второе или третье выражение (но не оба). Результат будет адресом, если второй и третий операнд одного типа и являются адресами. R.5.17 Операции присваивания Существует несколько операций присваивания, все они выполняются справа налево. Для всех них требуется, чтобы левым операндом был изменяемый адрес. Тип выражения присваивания совпадает с типом левого операнда. Результат операции присваивание - значение, хранящееся в левом операнде после того как произошло присваивание. Результат является адресом. выражение-присваивания: выражение-условия унарное-выражение операция-присваивания выражение-присваивания операция-присваивания: один из = *= /= %= += -= >>= <<= &= ^= |= При простом присваивании (=) значение выражения заменяет собой значение объекта, с которым сопоставляется левый операнд. Если оба операнда арифметического типа, правый операнд, прежде чем произойдет присваивание, преобразуется к типу левого операнда. Неявные преобразования к типу перечисления ($$R.7.2) не производятся, поэтому если левый операнд имеет тип перечисления, правый операнд должен быть таким же. Если левый операнд имеет тип указателя, правый операнд должен быть типа указателя или выражением-константой, дающим результат 0. Правый операнд преобразуется к типу левого операнда, прежде выполнения присваивания. Указатель типа T* const можно присваивать указателю типа T*, но обратное присваивание считается незаконным ($$R.7.1.6). Объекты типа const T или volatile T можно присваивать по адресу типа T или volatile T (см. так же $$R.8.4). Если левый операнд имеет тип указателя на член, правый операнд должен быть типа указатель на член или выражением-константой, дающим результат 0; перед присваиванием правый операнд преобразуется к типу левого операнда. Присваивание объектам класса X ($$R.9) задается функцией X::operator=() ($$R.13.4.3). Если пользователь не определил свою функцию X::operator=(), для присваивания используется стандартный вариант ($$R.12.8). Отсюда следует, что объект класса, который является прямым или непрямым производным от X, и однозначно описан как производный в части public ($$R.4.6), можно присвоить объекту X. Указатель на член класса B можно присваивать указателю на член того же типа класса D при условии, что D является прямым или непрямым производным класса B, и однозначно описан как производный в части public ($$R.10.1.1). Присваивание объекту типа "ссылка на T" сводится к присваиванию объекту типа T, который обозначается ссылкой. Выполнение выражение вида E1 op= E2 эквивалентно выполнению E1 = E1 op (E2), однако E1 вычисляется лишь один раз. В операциях += и -= левый операнд может быть указателем, в этом случае правый (целочисленный) операнд преобразуется так, как объяснялось в $$R.5.7. Все правые операнды и все левые операнды, не являющиеся ссылками, должны быть арифметического типа. Для объектов класса присваивание в общем случае не совпадает с инициализацией ($$R.8.4, $$R.12.1, $$R.12.6, $$R.12.8). R.5.18 Операция запятая Операции запятая выполняются слева направо. выражение: выражение-присваивания выражение, выражение-присваивания Пара выражений, разделенных запятой, вычисляется слева направо и значение левого выражения уничтожается. Все побочные эффекты вычисления левого выражения могут возникать до вычисления правого выражения. Тип и значение результата совпадают с типом и значением правого выражения. Результат является адресом, если таковым является правое выражение. В контекстах, где запятая имеет специальное значение, скажем в списке фактических параметров функции ($$R.5.2.2) или в списке инициализаторов ($$R.8.4), описанная здесь операция запятая может появляться только в скобках, например, вызов функции f(a, (t=3,t+2), c); содержит три параметра, причем второй имеет значение 5. R.5.19 Выражения-константы В нескольких местах описания С++ требуются выражения, которые дают в результате целочисленную константу, например: в задании границ массива ($$R.8.2.4), в выражениях case ($$R.6.4.2), для задания длины битового поля ($$R.9.6) и как инициализирующее значение элемента перечисления ($$R.7.2). выражение-константа: выражение-условия В конструкции выражение-константа могут участвовать: литералы ($$R.2.5), элементы перечисления, значения целочисленного типа со спецификацией const, инициализированные выражением-константой ($$R.8.4) и выражения sizeof. Константы с плавающей точкой ($$R.2.5.3) должны быть приведены к целочисленному типу. Допустимы только преобразования типа к целочисленному типу. В частности не допустимы функции, объекты классов, указатели и ссылки, если не считать их использования в sizeof. Операция запятая и операция присваивания не допустимы в выражении-константе. R.6 Операторы Все операторы, за исключением оговоренных случаев, выполняются один за другим. оператор: помеченный-оператор оператор-выражение составной-оператор выбирающий-оператор оператор-цикла оператор-перехода оператор-описания R.6.1 Помеченный оператор Оператор можно снабдить меткой. помеченный-оператор: идентификатор : оператор case выражение-константа : оператор default : оператор Использование идентификатора в качестве метки является ее определением. Идентификатор метки может использоваться помимо этого только в качестве указания перехода в операторе goto. Областью видимости метки является функция, в которой она появилась. Метки нельзя повторно описывать в пределах одной функции. Нельзя использовать метку в операторе goto до ее определения. Метки имеют свое пространство именования и они не вступают в коллизию с другими идентификаторами. Метки в case или default могут встречаться только в операторе переключателя. R.6.2 Оператор-выражение Чаще всего операторами бывают выражения; в этом случае оператор имеет такой вид: оператор-выражение: выражение opt ; Обычно операторы-выражения являются присваиваниями или вызовами функций. Все побочные эффекты выполнения оператора-выражения происходят до выполнения следующего оператора. Оператор-выражение с отсутствующим выражением называется пустым оператором. Он может пригодиться, если необходимо поставить метку перед самым концом составного оператора ({) или для задания пустого тела оператора цикла while ($$R.6.5.1). R.6.3 Составной оператор или блок Для тех случаев, когда вместо одного оператора нужно использовать несколько, предусмотрен составной оператор (иногда его называют "блок"). составной-оператор: { список-операторов opt } список-операторов: оператор список-операторов оператор Отметим, что описание считается оператором ($$R.6.7). R.6.4 Выбирающий оператор Выбирающие операторы выбирают одну из нескольких структур управления. выбирающий-оператор: if ( выражение ) оператор if ( выражение ) оператор else оператор switch ( выражение ) оператор Оператор в выбирающем-операторе не может быть описанием. R.6.4.1 Оператор if Выражение должно быть арифметического типа, или типа указателя, или типа класс, для которого существует однозначное преобразование в арифметический тип или тип указателя ($$R.12.3). Вычисляется выражение, и если оно имеет отличный от нуля результат, выполняется первый вложенный оператор. Если использована конструкция else и выражение дает результат 0, выполняется второй вложенный оператор. Неоднозначность в случае нескольких конструкциями else разрешается путем отнесения else к последнему встретившемуся if, для которого не было else. R.6.4.2 Оператор переключателя Оператор переключателя вызывает передачу управления на один из нескольких операторов в зависимости от значения выражения. Выражение должно быть целочисленного типа или типа класса, для которого существует однозначное преобразование к целочисленному типу ($$R.12.3). Выполняются стандартные целочисленные преобразования. Любой из операторов переключателя можно пометить одним или несколькими префиксами, имеющими вид: case выражение-константа : Здесь выражение-константа ($$R.5.19) приводится к преобразованному типу выражения переключателя. Никакие две константы из case одного переключателя не должны иметь одинаковое значение. В переключателе может быть только один префикс вида default: Операторы переключателя могут быть вложенными, тогда метки из case или default относятся к самому первому переключателю, объемлющему их. При выполнении оператора переключателя вычисляется выражение, и его значение сравнивается с каждой из констант вариантов (case). Если одна из этих констант равна значению выражения, то управление передается в оператор, идущий за этой константой. Если ни одна из констант не совпала со значением выражения, но есть префикс default, то управление передается на оператор с этим префиксом. Если префикса default нет, и совпадения не было, то не выполняется ни один из операторов переключателя. Если операторы, выполняемые в результате выбора, не приводят к каким-либо передачам управления, то программа продолжает выполняться "по меткам case и default" беспрепятственно. Выход из переключателя возможен с помощью оператора break (см. $$R.6.6.1). Обычно оператор, с которым имеет дело переключатель, бывает составным. Описания могут появиться в операторах переключателя. Однако переход ниже описания, в котором была явная или неявная инициализация, считается незаконным, если только описание не находится во внутреннем блоке, который обходится (т.е. полностью обходится при передаче управления, $$R.6.7). Отсюда следует, что описание с явной или неявной инициализацией должно содержаться во внутреннем блоке. R.6.5 Операторы цикла Эти операторы задают виды цикла. оператор-цикла: while ( выражение ) оператор do оператор while (выражение) for ( оператор-иниц выражение opt ; выражение opt ) оператор оператор-иниц: оператор-выражение оператор-описание Обратите внимание, что конструкция оператор-иниц кончается точкой с запятой. Оператор в операторе-цикла не должен быть описанием. R.6.5.1 Оператор while В операторе while вложенный оператор выполняется до тех пор, пока значение выражения не станет равным нулю. Проверка происходит перед каждым выполнением оператора. Выражение должно быть арифметического типа, или типа указателя, или типа класс, для которого существует однозначное преобразование в арифметический тип или тип указателя ($$R.12.3). R.6.5.2 Оператор do В операторе do вложенный оператор выполняется до тех пор, пока значение выражения не станет равным нулю. Проверка происходит после каждого выполнения оператора. Выражение должно быть арифметического типа, или типа указателя, или типа класс, для которого существует однозначное преобразование в арифметический тип или тип указателя ($$R.12.3). R.6.5.3 Оператор for Оператор for for (оператор-иниц выражение-1 opt ; выражение-2 opt ) оператор эквивалентен конструкции оператор-иниц while (выражение-1) { оператор выражение-2 ; } за исключением того факта, что оператор continue в операторе for вызовет выполнение выражение-2 перед тем& как начать повторное вычисление выражения-1. Таким образом, первый оператор задает инициализацию для цикла, первое выражение производит проверку, выполняемую перед каждым шагом цикла, так что цикл завершается, когда выражение становится нулем, а второе выражение обычно задает приращение, и оно добавляется после каждого шага цикла. Первое выражение должно иметь арифметический тип, или тип указателя, или тип класса, для которого существует однозначное преобразование к арифметическому типу или типу указателя ($$R.12.3). Могут быть опущены одно или оба выражения. Если отсутствует выражение-1, то эквивалентный цикл с while имеет условие while (1). Если оператор-иниц является описанием, область видимости имен, описанных в нем, простирается до конца блока, закрывающего оператор for. R.6.6 Операторы перехода Операторы перехода делают безусловную передачу управления. оператор-перехода: break ; continue ; return выражение opt ; goto идентификатор ; По выходе из области видимости (каким бы образом это не произошло) вызываются деструкторы ($$R.12.4) для всех объектов классов, построенных в этой области, которые еще не были уничтожены. Это относится как к явно описанным объектам, так и ко временным объектам ($$R.12.2). R.6.6.1 Оператор break Оператор break может встретиться только в операторе цикла или переключателе, он приводит к окончанию ближайшего из объемлющих его операторов цикла или переключателей. Управление передается на оператор, следующий непосредственно за заканчиваемым, если такой есть. R.6.6.2 Оператор continue Оператор continue может встретиться только в операторе цикла и приводит к передаче управления в заголовок ближайшего из объемлющих операторов цикла, т.е. в конец цикла. Более точно можно сказать, что в каждом из операторов: while (foo) { do { for (;;) { // ... // ... // ... contin: ; contin: ; contin: ; } } while (foo); } оператор continue, не относящийся ко внешним операторам цикла, эквивалентен оператору goto contin. R.6.6.3 Оператор return Возврат из функции в обратившуюся к ней функцию происходит с помощью оператора return. Оператор return без выражения можно использовать только в функциях, которые не возвращают значение, т.е. в функциях, возвращающих значение типа void, или в конструкторах ($$R.12.1) и деструкторах ($$R.12.4). Оператор return с выражением можно использовать только в функциях, которые возвращают значение. Значение выражения передается в ту функцию,которая вызвала данную функцию. Если нужно, значение преобразуется к типу функции, в которой выполняется return, по тем же правилам как при инициализации. Это может привести к вызову конструктора или копированию временных объектов ($$R.12.2). Выход из функции по концу эквивалентен возврату без выдаваемого значения, что является незаконным для функции, возвращающей значение. R.6.6.4 Оператор goto Оператор goto безусловно передает управление на оператор, помеченный идентификатором. Идентификатор должен быть меткой ($$R.6.1), находящейся в текущей функции. R.6.7 Оператор описания Оператор описания заводит в блоке новый идентификатор и имеет вид: оператор-описания: описание Если идентификатор, введенный с помощью описания, уже был ранее описан во внешнем блоке, внешнее описание становится скрытым до конца блока, после чего оно опять вступает в силу. Все инициализации автоматических (auto) и регистровых (register) переменных производятся каждый раз, когда выполняется оператор-описание. Уничтожение локальных переменных, описанных в блоке, происходит при выходе из блока ($$R.6.6). Уничтожение автоматических переменных, определенных в цикле, происходит на каждом шаге цикла. Например, переменная Index j создается и уничтожается каждый раз в течение цикла по i: for (int i = 0; i<100; i++) for (Index j = 0; j<100; j++) { // ... } Выход из цикла или из блока или переход, минуя инициализацию автоматических переменных, приводит к уничтожению автоматических переменных, описанных в точке, откуда происходит переход, но не в точке, куда происходит переход. Переход в блок возможен при условии, что он не приводит к пропуску инициализации. Считается незаконным переход, обходящий описание с явной или неявной инициализацией, кроме случаев, когда оно находится во внутреннем блоке, который пропускается (т.е. в него никогда не попадает управление) или переход происходит из той точки, где уже была инициализация переменной. Например, void f() { // ... goto lx; //ошибка: переход, минуя инициализацию // ... ly: X a = 1; // ... lx: goto ly; // нормально, за переходом будет вызов // деструктора для `a' } Автоматическая переменная, которая была создана при некотором условии, уничтожается при выполнении этого условия, и не может быть доступна вне проверки этого условия. Например, if (i) for (int j = 0; j<100; j++) { // ... } if (j!=100) // ошибка: обращение вне условия // ... ; Инициализация локального объекта с классом памяти static ($$R.7.1.1) производится прежде, чем управление пройдет через область его описания. Если статическая переменная инициализируется выражением, которое не является выражением-константой, то перед первым входом в блок происходит стандартная инициализация нулем, приведенным к нужному типу ($$R.8.4). Деструктор для локального статического объекта будет вызываться в том и только в том случае, если переменная была создана с помощью конструктора. Деструктор должен вызываться сразу перед вызовом или как составная часть вызова функций, заданных в atexit() ($$R.3.4). R.6.8 Разрешение неоднозначности Существует неоднозначность в грамматике языка, касающаяся оператора-выражения и описания, а именно, оператор-выражение, содержащий как самое левое подвыражение явное преобразование типа, заданное в функциональном стиле ($$R.5.2.3), может быть не отличим от описания, в котором первый описатель начинается со (. В таких случаях оператор считается описанием. Для разрешения неоднозначности следует исследовать весь оператор, чтобы определить является он оператором-выражением или описанием. Так устраняется неоднозначность во многих случаях. Например, пусть T - имя-простого-типа ($$R.7.1.6), тогда имеем T(a)->m = 7; // оператор-выражение T(a)++; // оператор-выражение T(a,5)<<c; // оператор-выражение T(*e)(int); // описание T(f)[]; // описание T(g) = {1, 2 }; // описание T(*d)(double(3)); // описание Остальные случаи представляют описания. Например, T(a); // описание T(*b)(); // описание T(c)=7; // описание T(d),e,f=3; // описание T(g)(h,2); // описание Неоднозначность здесь чисто синтаксическая, т.е. на ее разрешение не влияет тот факт, является ли имя именем-типа или нет. Есть другой вид коллизии между оператором-выражением и описанием, который разрешается требованием, чтобы описание функции в блоке ($$R.6.3) сопровождалось именем-типа, например: void g() { int f(); // описание int a; // описание f(); // оператор-выражение a; // оператор-выражение } R.7 Описания Описания используются для интерпретации каждого из идентификаторов; необязательно, чтобы они сопровождались выделением памяти, сопоставляемой с идентификатором. Описания имеют вид описания: спецификации-описания opt список-описателей opt ; описание-asm определение-функции спецификация-связи Описатели в списке-описателей ($$R.8) содержат описываемые идентификаторы. Конструкция спецификации-описания может отсутствовать только в определении функций ($$R.8.3) или в описании функций. Список-описателей может быть пустым, только при описании класса ($$R.9) или перечисления ($$R.7.2), т.е. когда спецификация-описания есть спецификация-класса или спецификация-перечисления. Конструкция описание-asm объясняется в $$R.7.3, а спецификация-связи в $$R.7.4. Описание происходит в определенной области видимости ($$R.3.2), правила области видимости приводятся в $$R.10.4. R.7.1 Спецификации В описании можно использовать следующие спецификации: спецификация-описания: спецификация-класса-памяти спецификация-типа спецификация-fct спецификация-шаблона-типа friend typedef спецификации-описания: спецификации-описания opt спецификация-описания Самая длинная последовательность конструкций спецификация-описания, которая, возможно, является именем типа, образует в описании конструкцию спецификации-описания. Последовательность должна быть согласованной, что объясняется ниже. Например, typedef char* Pc; static Pc; // ошибка: нет имени Здесь описание static Pc является незаконным, поскольку не указано никакого имени статической переменной типа Pc. Чтобы иметь переменную типа int с именем Pc, необходимо задать спецификацию-типа int, чтобы показать, что (пере)определяется имя Pc из typedef, а не просто Pc является одним из элементов последовательности конструкций спецификация-описания, например, void f(const Pc); // void f(char* const) void g(const int Pc); // void g(const int) Укажем, что поскольку signed, unsigned, long и short по умолчанию трактуются как int, конструкция имя-typedef, которая появляется после одной из перечисленных спецификаций типа, должна задавать (пере)определяемое имя, например, void h(unsigned Pc); // void h(unsigned int) void k(unsigned int Pc); // void k(unsigned int) R.7.1.1 Спецификации класса памяти Спецификации класса памяти могут быть такие: спецификация-класса-памяти: auto register static extern Спецификации auto и register могут применяться только для имен объектов, которые описаны в блоке ($$R.6.3), или для формальных параметров ($$R.8.3). Почти всегда спецификация auto избыточна и используется не часто, так, auto используется, чтобы явно отделить оператор-описание от оператора-выражения ($$R.6.2). Описание register является описанием auto, которое подсказывает транслятору, что описываемые переменные будут использоваться достаточно интенсивно. Подсказка может быть проигнорирована, и во многих реализациях она игнорируется в том случае, когда берется адрес переменной. Описание объекта считается определением, если только оно не содержит спецификации extern и инициализации ($$R.3.1). Определение приводит к выделению памяти соответствующего размера и выполнению соответствующей инициализации ($$R.8.4). Спецификации static и extern могут применяться только к именам объектов или функций или к анонимным объединениям. Внутри блока недопустимы описания функций со спецификацией static или формальных параметров со спецификацией static или extern. Статические члены класса описываются в $$R.9.4. Спецификация extern недопустима для членов класса. Имя со спецификацией static подлежит внутреннему связыванию. Объекты, описанные как const, подлежат внутреннему связыванию, если только они не были описаны с внешней связью. Имя со спецификацией extern подлежит внешнему связыванию, если только ранее оно не было описано с внутренней связью. Имя с файловой областью видимости и без спецификации-класса-памяти подлежит внешнему связыванию, если только ранее оно не было описано с внутренней связью или со спецификацией const. В смысле связывания для функций, не являющихся членами, спецификация inline эквивалентна static ($$R.3.3). Для одного имени все его спецификации, определяющие связывание, должны быть согласованы. Например, static char* f(); // f() имеет внутреннее связывание char* f() // f() все еще внутреннее { /* ... */ } char* g(); // g() имеет внешнее связывание static char* g() // ошибка: противоречие в связывании { /* ... */ } static int a; // `a' имеет внутреннее связывание int a; // ошибка: второе определение static int b; // `b' имеет внутреннее связывание extern int b; // `b' все еще внутреннее int c; // `c' имеет внешнее связывание static int c; // ошибка: противоречие в связывании extern int d; // `d' имеет внешнее связывание static int d; // ошибка: противоречие в связывании Имя неопределенного класса можно использовать в опис