Бьeрн Страустрап. Справочное руководство по C++
страница №6
...late<class T>void f() { // ошибка
T a;
// ...
}
Все параметры-шаблона-типа, приведенные в шаблоне типа для функции,
должны быть параметрами-типа.
R.14.5 Описания и определения
Для каждого имени шаблона типа в программе должно существовать только
одно определение. Описаний может быть несколько. Определение
используется для создания специальных шаблонных классов и шаблонных
функций, которые будут соответствовать шаблону типа.
Конструкция имя-шаблонного-класса вводит описание шаблонного класса.
Вызов шаблонной функции или взятие ее адреса вводит описание
шаблонной функции. Для вызова или взятия адреса шаблонной функции
в языке существует особое соглашение: имя шаблонной функции
используется точно так же как имя обычной функции. Описание функции
с таким же именем, как у шаблонной функции, и с сопоставимыми типами
параметров, вводит описание специальной шаблонной функции.
Если для выполнения некоторых операций требуется определение
специального шаблонного класса или специальной шаблонной функции,
и если такого определения в программе нет, то оно будет создано.
Определение обычной (нешаблонной) функции с типом, который
точно сопоставляется с типом из описания шаблонной функции,
считается определением специальной шаблонной функции. Рассмотрим
пример:
template<class T> void sort(vector<T>& v) { /* ... */ }
void sort(vector<char*>& v) { /* ... */ }
Здесь определение функции sort будет использоваться для той функции
из семейства sort, которая сопоставляется при вызове с типом
параметра vector<char*>. Для других типов vector будет создаваться
соответствующее им определение функции по шаблону типа.
Можно определить класс, который задает шаблонный класс, например:
template<class T> class stream { /* ... */ };
class stream<char> { /* ... */ };
Здесь описание класса будет использоваться в качестве определения
потока символов (stream<char>). Другие потоки будут управляться
с помощью шаблонных функций, создаваемых по шаблону типа для функций.
Пока не появится описание шаблона типа для класса, никакие операции,
которым требуется определенный класс, не могут быть произведены над
шаблонным классом. После этого специальный шаблонный класс
будет считаться определенным, причем сразу же перед первым глобальным
описанием, использующим его имя.
R.14.6 Функции-члены шаблонов типа
Функция-член шаблонного класса считается неявной шаблонной функцией,
а параметры шаблона типа для ее класса - ее шаблонными параметрами.
Приведем пример, в котором описаны три шаблона типа для функции:
template<class T> class vector {
T* v;
int sz;
public:
vector(int);
T& operator[](int);
T& elem(int i) { return v[i]; }
// ...
};
Функцию, выполняющую индексацию, можно определить следующим образом:
template<class T> T& vector<T>::operator[](int i)
{
if (i<0 || sz>=i) error("vector: range error");
return v[i];
}
Шаблонный параметр для vector<T>::operator[]() будет задаваться
тем типом vector, к которому применяется операция индексации.
vector<int> v1(20);
vector<complex> v2(30);
v1[3] = 7; // vector<int>::operator[]()
v2[3] = complex(7,8); // vector<complex>::operator[]()
R.14.7 Друзья
Функция-друг для шаблона типа не является неявной шаблонной функцией,
например:
template<class T> class task {
// ...
friend void next_time();
friend task<T>* preempt(task<T>*);
friend task* prmt(task*); // ошибка
// ...
};
Здесь функция next_time() становится другом всех классов task, а
каждый класс task имеет в качестве друга функцию preempt() c
соответствующими типами параметров. Функцию preempt() можно
определить как шаблон типа.
template<class T>
task<T>* preempt(task<T>* t) { /* ... */ }
Описание функции prmt() является ошибочным, поскольку типа
task не существует, а есть только специальные шаблонные типы
task<int>, task<record>, и т.д.
R.14.8 Статические члены и переменные
Для каждого шаблонного класса или функции, создаваемых по шаблону
типа, образуется своя копия статических переменных или членов.
Рассмотрим пример:
template<class T> class X {
static T s;
// ...
};
X<int> aa;
X<char*> bb;
Здесь в классе X<int> есть статический член типа int, а в классе
X<char> есть статический член типа char*.
Аналогично, в приведенном ниже примере, функция f(int*) имеет
статический член s типа int, а функция f(char**) имеет статический
член типа char**:
template<class T> f(T* p)
{
static T s;
// ...
}
void g(int a, char* b)
{
f(&a);
f(&b);
}
R.15 Обработка особых ситуаций
R.15.1 Обработка особых ситуаций
При обработке особых ситуаций в ходе выполнения программы информация и
управление передаются из некоторой точки обработчику особых
ситуаций. Обработчик находится в цепочке выполненных вызовов
функций. Управление обработчику передается с помощью
выражения-запуска, которое может быть только в проверяемом-блоке
обработчика или в функции, вызванной из проверяемого-блока.
проверяемый-блок:
try составной-оператор список-обработчиков
список-обработчиков:
обработчик список-обработчиков opt
обработчик:
catch ( описание-особой-ситуации ) составной-оператор
описание-особой-ситуации:
список-спецификаций-типа описатель
список-спецификаций-типа абстрактный-описатель
список-спецификаций-типа
...
выражение-запуска:
throw выражение opt
Конструкция проверяемый-блок является оператором ($$R.6), а
выражение-запуска - унарным выражением типа void ($$R.5). Иногда
выражение-запуска называют "точкой запуска", а про функцию, в которой
встретилось выражение-запуска, говорят, что она "запускает особую
ситуацию. Часть программы, которой передается управление из точки
запуска называется обработчиком.
R.15.2 Запуск особой ситуации
При запуске особой ситуации управление передается обработчику. Запуск
сопровождается передачей объект, тип которого определяет, какой
обработчик должен перехватить особую ситуацию. Так, выражение
throw "Help!";
может быть перехвачено некоторым обработчиком с типом char*:
try {
// ...
}
catch(const char* p) {
// здесь обрабатывается особая ситуация в символьных строках
}
а особая ситуация Overflow (переполнение):
class Overflow {
// ...
public:
Overflow(char,double,double);
};
void f(double x)
{
// ...
throw Overflow('+',x,3.45e107);
}
может быть перехвачена обработчиком
try {
// ...
f(1.2);
// ...
}
catch(Overflow& oo) {
// здесь обработка особой ситуации типа Overflow
}
При запуске особой ситуации управление передается ближайшему
обработчику соответствующего типа. "Ближайший" - это обработчик,
проверяемый-блок которого последним получил управление и оно еще
не было передано оттуда. Что такое "соответствующий" тип
определяется в $$R.15.4.
При выполнении выражения-запуска создается временный объект
статического типа, который служит операндом в команде throw,
Этот объект используется для инициализации переменной,
соответствующего типа, описанной в обработчике. Если не считать
ограничений на сопоставление типов (см. $$R.15.4) и использование
временной переменной, то операнд throw аналогичен параметру функции
при вызове ($$R.5.2.2) или операнду в операторе return.
Если можно было бы, не меняя смысла программы за счет отказа
от вызовов конструкторов и деструкторов для временного объекта
($$R.12.1), обойтись совсем без временного объекта, то особую
ситуацию можно было бы непосредственно инициализировать в обработчике
параметром выражения запуска.
Если в выражении-запуска операнд не задан, то происходит
перезапуск обработки особой ситуации. Такое выражение-запуска
может появится только в самом обработчике или в функции, которая
непосредственно или опосредованно вызывается из него.
Например, фрагмент программы, который выполняется при обработке особой
ситуации, если нельзя еще полностью провести эту обработку, может
выглядеть так:
try {
// ...
}
catch (...) { // перехват всех особых ситуаций
// (частичная) обработка особых ситуаций
throw; // передача остальных особых ситуаций другому обработчику
}
R.15.3 Конструкторы и деструкторы
Когда управление передается из точки запуска особой ситуации
обработчику, то вызываются деструкторы для всех автоматических
объектов, построенных с момента входа в проверяемый-блок.
Если объект не был построен полностью, то деструкторы
вызываются только для полностью построенных вложенных в него объектов.
Кроме того, если особая ситуация запускается в конструкторе при
построении элемента автоматического массива, то уничтожаться будут
только уже построенные элементы этого массива.
Процесс вызова деструкторов для уничтожения автоматических
объектов, построенных в ходе выполнения программы от начала
проверяемого-блока до выражения-запуска, называется "раскручиванием
стека".
R.15.4 Обработка особой ситуации
Обработчик типа T, const T, T& или const& сопоставим с
выражением-запуска, имеющим операнд типа E, если:
[1] T и E являются одним типом;
[2] T является доступным ($$R.4.6) базовым классом E в точке
запуска;
[3] T является типом указателя, а E является таким типом указателя,
который можно в точке запуска преобразовать в T с помощью
стандартных преобразований указателя ($$R.4.6).
Рассмотрим пример:
class Matherr { /* ... */ virtual vf(); };
class Overflow : public Matherr { /* ... */ };
class Underflow : public Matherr { /* ... */ };
class Zerodivide : public Matherr { /* ... */ };
void f()
{
try {
g();
}
catch (Overflow oo) {
// ...
}
catch (Matherr mm) {
// ...
}
}
Здесь обработчик Overflow будет перехватывать ситуации типа
Overflow, а обработчик Matherr будет перехватывать ситуации типа
Matherr и всех типов, являющихся общими производными от Matherr,
включая Underflow и Zerodivide.
Обработчики в проверяемом-блоке подбираются для данной особой
ситуации в порядке их описания. Считается ошибкой , если обработчик
для базового класса помещен перед обработчиком для производного класса,
поскольку при таком расположении управление никогда не попадет
к обработчику для производного класса.
Эллипсис ... в описании-особой-ситуации действует так же как, и
в описании параметров функции, он сопоставим с любой особой
ситуацией. Если задан эллипсис, то использующий его обработчик
должен идти последним в проверяемом-блоке.
Если в проверяемом-блоке не произошло сопоставления ни с одним
из обработчиков, поиск соответствующего обработчика продолжается
в динамически объемлющем проверяемом-блоке. Если во всей программе
не произошло сопоставления ни с одним обработчиком, вызывается
функция terminate() ($$R.15.7).
Особая ситуация считается обработанной после входа в тело
обработчика. В этот момент завершится "раскручивание стека".
R.15.5 Спецификации особых ситуаций
Возникновение и перехватывание особой ситуации влияет на
взаимодействие функций. Список особых ситуаций, которые прямо или
косвенно может запустить данная функция, можно задать как часть ее
описания. Конструкция спецификация-особой-ситуации
предшествует описателю функции.
спецификация-особой-ситуации:
throw ( список-типов opt )
список-типов:
имя-типа
список-типов , имя-типа
Приведем пример:
void f() throw (X,Y)
{
// ...
}
Если функция попробует запустить неуказанную в списке ситуацию,
управление передается функции unexpected(), см. $$R.15.8.
Реализация языка не должна запрещать выражение только потому,
что при его вычислении возможен запуск особой ситуации, не указанной
в спецификации-особой ситуации описания функции. Обработка непредвиденных
особых ситуаций происходит в динамике.
Функция, в которой отсутствует спецификация-особой-ситуации,
может запустить любую особую ситуацию.
Функция с пустой спецификацией-особых-ситуаций (throw()) не должна
запускать никаких особых ситуаций.
Если функция может запустить особую ситуацию класса X, то она
может запустить особую ситуацию любого класса, являющегося общим
производным классом от X.
Конструкция спецификация-особой-ситуации не относится к типу
функции.
R.15.6 Специальные функции
Механизм управления особыми ситуациями использует для реакции на ошибки
при самой обработке особых ситуаций функции:
terminate() и unexpected().
R.15.6.1 Функция terminate()
Иногда от предусмотренной обработки особых ситуаций приходится
переходить к более грубым приемам, например:
- когда механизм управления особыми ситуациями не смог найти
обработчик для запущенной особой ситуации;
- когда механизм управления особыми ситуациями столкнулся
с нарушенной структурой стека;
- когда деструктор, вызванный в процессе раскрутки стека при запуске
особой ситуации, сам пытается завершить выполнение программы,
запустив особую ситуацию.
В этих случаях вызывается функция
void terminate();
Она в свою очередь вызывает функцию, которая была указана как параметр
при последнем обращении к set_terminate():
typedef void(*PFV)();
PFV set_terminate(PFV);
Функция, которая была задана в предыдущем вызове set_terminate(),
будет возвращаемым значением текущего вызова. Это помогает
пользователю реализовать алгоритм восстановления стека. По умолчанию
функция terminate() вызывает abort().
Выбор с помощью terminate() такой функции, которая вместо
действительного завершения программы, пытается вернуться в
вызвавшую программу, является ошибкой.
R.15.6.2 Функция unexpected()
Если функция, имеющая спецификацию-особой-ситуации, запускает
неуказанную особую ситуацию, то вызывается функция
void unexpected();
Она в свою очередь вызывает функцию, которая была задана как
параметр при последнем обращении к set_unexpected():
typedef void(*PFV)();
PFV set_unexpected(PFV);
Функция, которая была задана в предыдущем вызове set_unexpected(),
будет возвращаемым значением текущего вызова. Это помогает
пользователю реализовать алгоритм восстановления стека. По умолчанию
unexpected() вызывает terminate(). Поскольку по умолчанию
terminate() вызывает abort(), результатом будет непосредственное
и точное обнаружение ошибки.
R.15.7 Особые ситуации и правила доступа
Для формального параметра операции catch действуют такие же
правила доступа, как и для формального параметра функции, в которой
задана операция catch.
При запуске особой ситуации можно указывать такой объект, который
можно копировать и уничтожать в области видимости функции, где
задана операция throw.
R.16 Препроцессорная обработка
Реализация языка С++ включает препроцессор с возможностями
макроподстановки, условной трансляции и включения указанных
файлов.
Для передачи заданий препроцессору служат строки, начинающиеся
с символа # (перед ним могут идти пробелы и символы горизонтальной
табуляции). Такие строки называются командами, и их синтаксис
определяется независимо от остального языка. Команды могут
находиться в любом месте программы, и их действие продолжается
(независимо от правил областей видимости С++) до конца данной
единицы трансляции ($$R.2).
Команду препроцессора, как и любую строку, можно продолжить на
следующей строке входного текста, поместив символ обратной
дробной черты непосредственно перед символом конца продолжаемой
строки. Препроцессор до того, как входная строка будет разбита на
лексемы, удаляет символы обратной дробной черты и конца строки.
Символ обратной дробной черты не должен быть последним символом
входного файла.
К лексемам препроцессора относятся: лексемы самого языка ($$R.2.1),
имя файла, которое используется в команде #include и вообще любой
символ, отличный от обобщенного пробела и несовпадающий ни с какой
из лексем препроцессора.
R.16.1 Фазы препроцессорной обработки
По определению существует несколько фаз препроцессорной обработки.
В конкретной реализации фазы могут сливаться, но результат все равно
должен быть таким, как будто были выполнены все фазы.
Перечислим их.
При необходимости символы, зависящие от системы символы, обозначающие
конец строки, заменяются на стандартный символ конца строки. Аналогичной
замене подлежат все зависящие от системы символы. Определенные
последовательности символов (триграфы) заменяются на эквивалентный
им отдельный символ ($$R.16.2).
Удаляются все такие пары символов: обратная дробная черта, следующий
за ней символ конца строки. В результате будут слиты строки входного
текста, из которых была удалена эта пара.
Входной текст разбивается на лексемы препроцессора и последовательность
обобщенных пробелов. Каждый комментарий заменяется на один пробел.
Входной текст не должен кончаться посреди лексемы или комментария.
Выполняются команды препроцессора, и производятся макроподстановки
($$R.16.3, $$R.16.4, $$R.16.5, $$R.16.6, $$R.16.7 и $$R.16.8).
В символьных константах и строках литералов комбинации специальных
символов заменяются на свои эквиваленты ($$R.2.5.2).
Сливаются соседние строки литералов.
Результат препроцессорной обработки подвергается синтаксическому
и семантическому анализу, транслируется, а затем связывается с
необходимыми библиотеками и другими программами.
R.16.2 Триграфные последовательности
Прежде чем начнется какая-либо иная препроцессорная обработка,
каждое вхождение триграфной последовательности заменяется на один
символ в соответствии с приведенной ниже таблицей.
??= # ??( [
??/ \ ??) [
??' ^ ??! |
Например, строка
??=define arraycheck(a,b) a??(b??) ??!??! b??(a??)
преобразуется в
#define arraycheck(a,b) a[b] || b[a]
R.16.3 Макроопределение и макроподстановка
Команда вида
#define идентификатор строка-лексем
называется макроопределением. Она указывает препроцессору, что надо
произвести замену всех последующих вхождений идентификатора на заданную
последовательность лексем, называемую строкой замены. Обобщенные
пробелы, окружающие эту последовательность лексем, отбрасываются.
Например, при определении
#define SIDE 8
описание
char chessboard[side][side];
после макроподстановки примет вид
char chessboard[8][8];
Определенный таким способом идентификатор можно переопределить
с помощью другой команды #define, но при условии, что строки
замены в обоих определениях совпадают. Все символы обобщенного
пробела, разделяющие лексемы, считаются идентичными.
Команда вида
идентификатор ( идентификатор, ... ,идентификатор) строка-лексем
называется макроопределением с параметрами или "функциональным"
макроопределением. В нем недопустимы пробелы между первым
идентификатором и символом (. Определенный таким способом
идентификатор можно переопределить с помощью другого функционального
макроопределения, но при условии, что во втором определении
то же число и те же наименования параметров, что и в первом, а обе
строки замены совпадают. Все символы обобщенного пробела, разделяющие
лексемы, считаются идентичными.
Последующие вхождения идентификатора, определенного в
функциональном макроопределении, если за ним следуют символ
(, последовательность лексем, разделенных запятыми, и символ
), заменяются на строку лексем из макроопределения. Обобщенные
пробелы, окружающие строку замены, отбрасываются. Каждое вхождение
идентификатора, из списка параметров макроопределения, заменяется
на последовательность лексем, представляющую соответствующий
фактический параметр в макровызове. Фактическими параметрами являются
строки лексем, разделенные запятыми. Запятая, взятая в кавычки, или
находящаяся в символьной константе или во вложенных круглых скобках,
не разделяет параметров. Число фактических параметров макровызова должно
совпадать с числом параметров макроопределения.
После идентификации параметров для функционального макроопределения
происходит подстановка фактических параметров. После выполнения подстановок
в параметре (если они были) этот параметр в строке замены замещается
фактическим параметром из макровызова ($$R.16.3.3); исключения
составляют случаи, когда параметру предшествует лексема # ($$R.16.3.1),
или с ним соседствует лексема ## ($$R.16.3.2).
Приведем пример. Пусть есть макроопределения
#define index_mask 0XFF00
#define extract(word,mask) word & mask
Тогда макровызов
index = extract(packed_data,index_mask);
после подстановки примет вид
index = packed_data & 0XFF00;
Для обоих видов макроопределений строка замены проверяется на
наличие других макроопределений ($$R.16.3.3).
R.16.3.1 Операция #
Если непосредственно перед параметром в строке замены идет лексема
#, то при подстановке параметр и операция # будут заменены
на строку литералов , содержащую имя соответствующего параметра
макровызова. В символьной константе или строке литералов, входящих
в параметр, перед каждым вхождением \ или " вставляется символ \.
Например, если есть макроопределения
#define path(logid,cmd) "/usr/" #logid "/bin/" #cmd
то макровызов
char* mytool=path(joe,readmail);
приведет к такому результату:
char* mytool="/usr/" "joe" "/bin/" "readmail";
После конкатенации соседних строк ($$R.16.1) получим:
char* mytool="/usr/joe/bin/readmail";
R.16.3.2 Операция ##
Если в строке замены между двумя лексемами, одна из которых
представляет параметр макроопределения, появляется операция
##, то сама операция ## и окружающие ее обобщенные пробелы
удаляются. Таким образом, результат операции ## состоит в
конкатенации.
Пусть есть макроопределение,
#define inherit(basenum) public Pubbase ## basenum, \
private Privbase ## basenum
тогда макровызов
class D : inherit(1) { };
приведет к такому результату:
class D : public Pubbase1, Privbase1 { };
Макроопределение, которое в строке замены соседствует с
##, не подлежит подстановке, однако, результат конкатенации может
использоваться для подстановки. Приведем пример. Пусть есть
определения:
#define concat(a) a ## ball
#define base B
#define baseball sport
Тогда макровызов
concat(base)
даст в результате
sport
а вовсе не
Bball
R.16.3.3 Повторный просмотр и дальнейшие подстановки
После того, как в строке замены произошла подстановка всех параметров
макровызова, получившаяся строка просматривается повторно для
обнаружения дополнительных макроопределений. Если в процессе
повторных просмотров строки замены найдено имя макроопределения, то
подстановка все же не происходит.
Рекурсивную подстановку нельзя выполнить как команду
препроцессора, хотя она кажется для него естественной командой.
R.16.3.4 Область видимости макроимен и конструкция #undef
После появления макроопределения идентификатор из него считается
определенным и остается в текущей области видимости (независимо от
правил областей видимости в С++) до конца единицы трансляции или
пока его определение не будет отменено с помощью команды #undef.
Команда #undef имеет вид:
#undef идентификатор
Она заставляет препроцессор "забыть" макроопределение с этим
идентификатором. Если указанный идентификатор не является
определенным в данный момент макроименем, то команда #undef
игнорируется.
R.16.4 Включение файлов
Управляющая строка вида:
#include <имяфайла>
приводит к замене данной строки на содержимое файла с указанным именем.
Поиск указанного файла проходит в определенной последовательности
частей архива системы и определяется реализацией.
Аналогично, управляющая строка вида:
#include "имяфайла"
приводит к замене данной строки на содержимое файла с указанным
именем. Поиск этого файла начинается в особых (системных) частях
архива, указанных в начале последовательности поиска. Если там он
не найден, то поиск файла идет по всей последовательности, как если бы
управляющая строка имела вид:
#include <имяфайла>
В имени файла, ограниченном символами < и > нельзя использовать
символы конца строки или >. Если в таком имени появится один из
символов ', \, или ", а также последовательность символов /* или //,
то результат считается неопределенным.
В имени файла, ограниченном парой символов " нельзя использовать
символы конца строки или ", хотя символ > допустим. Если в таком
имени появится символ ' или \ или последовательность /* или //,
то результат считается неопределенным.
Если команда
#include строка-лексем
имеет вид, соответствующий ни первой, ни второй управляющей строке,
то лексемы препроцессора, заданные в этой команде обрабатываются как
обычный текст. В результате должна получиться команда, вид которой
соответствует одному из приведенных. Она и будет выполнена как положено.
Команда #include может быть в файле, который сам появился в
результате выполнения другой команды #include.
Реализация может накладывать ограничение на глубину вложенности
команды #include во входных файлах программы, которые приходится
читать для выполнения первоначальной команды #include в одном из
входных файлов.
R.16.5 Условная трансляция
С помощью препроцессора можно организовать условную трансляцию
программы. Синтаксически это задается следующим образом:
условное:
часть-if части-elif opt часть-else opt строка-endif
часть-if:
строка-if текст
строка-if:
# if выражение-константа
# ifdef идентификатор
# ifndef идентификатор
части-elif:
строка-elif текст
части-elif строка-elif текст
строка-elif:
# elif выражение-константа
часть-else:
строка-else текст
строка-else:
# else
строка-endif:
# endif
Константные выражения в #if и #elif (если эти части есть)
вычисляются в порядке их задания в тексте до тех пор, пока одно
из них не окажется отличным от нуля. Операторы С++, следующие за
строкой, в которой выражение оказалось равным нулю, не транслируются.
Команды препроцессора, идущие за этой строкой игнорируются.
После того, как найдена команда с ненулевым значением выражения,
текст всех последующих частей #elif и #else (т.е. операторы С++ и
команды препроцессора) игнорируется. Текст, относящийся к первой
команде с ненулевым значением выражения подлежит обычной препроцессорной
обработке и трансляции. Если значения всех выражений, указанных в #if
и #elif, оказались равными нулю, тогда обычной обработке подлежит текст,
относящийся к #else.
В выражении-константе, которое встретилось в #if или #elif
можно использовать унарную операцию defined, причем в двух
вариантах:
defined идентификатор
или
defined (идентификатор)
Если эта операция применяется к идентификатору, который был определен
с помощью команды #define, и если это определение не было отменено
командой #undef, то результат равен 1, иначе результат равен 0.
Сам идентификатор defined нельзя переопределить, нельзя и отменить
его определение.
После применения операций defined происходит раскрытие всех
всех макроопределений, имеющихся в константном выражении
см. $$R.16.3. В результате должно получиться целочисленное
выражение-константа, отличающееся от определения в $$R.5.19 тем,
что типы int и unsigned int рассматриваются как long и unsigned long
соответственно, а кроме того в этом выражении не должно быть
операций приведения, sizeof или элемента перечисления.
Управляющая строка
#ifdef идентификатор
эквивалентна строке
#if defined идентификатор
а управляющая строка
#ifndef идентификатор
эквивалентна строке
#if !defined идентификатор
Конструкции, задающие условную трансляцию, могут быть вложенными,
но реализация может накладывать ограничение на глубину вложенности
этих конструкций.
R.16.6 Управление строками
Для удобства написания программ, порождающих текст на С++, введена
управляющая строка вида:
#line константа "имяфайла" opt
Она задает значение предопределенному макроимени __LINE__ ($$R.16.10),
которое используется в диагностических сообщениях или при
символической отладке; а именно: номер следующей строки
входного текста считается равным заданной константе, которая должна
быть десятичным целым числом. Если задано "имяфайла", то значение
макроимени __FILE__ ($$R.16.10) становится равным имени указанного
файла. Если оно не задано, __FILE__ не меняет своего значения.
Макроопределения в этой управляющей строке раскрываются до
выполнения самой команды.
R.16.7 Команда error
Строка вида:
#error строка-лексем
заставляет реализацию выдать диагностическое сообщение, состоящее
из заданной последовательности лексем препроцессора.
R.16.8 Команда pragma
Строка вида:
#pragma строка-лексем
заставляет реализацию вести себя некоторым определенным образом
при условии что реализация "понимает" эту строку. Любая
нераспознанная строка #pragma игнорируется.
R.16.9 Пустая директива
Команда препроцессора вида
#
не оказывает никакого действия.
R.16.10 Предопределенные макроимена
В процессе трансляции определенную информацию содержат следующие
предопределенные макроимена.
__LINE__ десятичная константа, содержащая номер текущей строки
текста программы на С++.
__FILE__ строка литералов, представляющая имя транслируемого
входного файла.
__DATE__ строка литералов, представляющая дату трансляции в виде
"Mmm dd yyyy" или "Mmm d yyyy", если число меньше 10,
(здесь Mmm задает месяц, dd - день, а yyyy - год).
__TIME__ строка литералов, представляющая время трансляции в виде
"hh:mm:ss", (здесь hh задает часы, mm - минуты, а
ss - секунды).
Кроме того, считается определенным при трансляции программы на С++
макроимя __cplusplus.
Перечисленные макроимена нельзя как переопределять, так и отменять их
определения.
Макроимена __LINE__ и __FILE__ можно определить с помощью команды
#line ($$R.16.6).
Определено ли макроимя __STDC, и если да, то каково его значение,
зависит от реализации.
R.17 Приложение A: Сводка грамматики
Это приложение не относится к справочному руководству языка и
не является определением конструкций C++.
Она только должно служить более полному пониманию С++. Нельзя
рассматривать его как точное определение языка, так как описанная здесь
грамматика допускает произвольное множество конструкций, каждая из
которых законна для С++. Чтобы различать выражения и описания,
следует применять правила разрешения неопределенности ($$r.6.8,
$$R.7.1, $$R.10.1.1). Далее, для отсеивания синтаксически правильных,
но бессмысленных, конструкций следует применять правила контроля
доступа, разрешения неопределенности и контроля типа.
R.17.1 Служебные слова
В описаниях: typedef ($$R.7.1.3), класса ($$R.9), перечисления
($$R.7.2), шаблона типа - ($$R.14) введены новые, зависящие от
контекста, служебные слова, а именно:
имя-класса:
идентификатор
имя-перечисления:
идентификатор
имя-typedef:
идентификатор
Отметим, что имя-typedef, обозначающее класс, является в то же
время конструкцией имя-класса ($$R.9.1).
R.17.2 Выражения
выражение:
выражение-присваивания
выражение, выражение-присваивания
выражение-присваивания:
выражение-условия
унарное-выражение операция-присваивания выражение-присваивания
операция-присваивания: один из
= *= /= %= += -= >>= <<= &= ^= |=
выражение-условия:
логическое-выражение-ИЛИ
логическое-выражение-ИЛИ ? выражение : выражение-условия
логическое-выражение-ИЛИ:
логическое-выражение-И
логическое-выражение-ИЛИ || логическое-выражение-И
логическое-выражение-И:
выражение-ИЛИ
логическое-выражение-И && выражение-ИЛИ
выражение-ИЛИ:
выражение-исключающего-ИЛИ
выражение-ИЛИ | выражение-исключающего-ИЛИ
выражение-исключающего-ИЛИ:
выражение-И
выражение-исключающего-ИЛИ ^ выражение-И
выражение-И:
выражение-равенства
выражение-И & выражение-равенства
выражение-равенства:
выражение-отношения
выражение-равенства == выражение-отношения
выражение-равенства != выражение-отношения
выражение-отношения:
сдвиговое-выражение
выражение-отношения < сдвиговое-выражение
выражение-отношения > сдвиговое-выражение
выражение-отношения <= сдвиговое-выражение
выражение-отношения >= сдвиговое-выражение
сдвиговое-выражение:
аддитивное-выражение
сдвиговое-выражение << аддитивное выражение
сдвиговое-выражение >> аддитивное выражение
аддитивное-выражение:
мультипликативное-выражение
аддитивное выражение + мультипликативное-выражение
аддитивное-выражение - мультипликативное-выражение
мультипликативное-выражение:
выражение-pm
мультипликативное-выражение * выражение-pm
мультипликативное-выражение / выражение-pm
мультипликативное-выражение % выражение-pm
выражение-pm:
выражение-приведения
выражение-pm .* выражение-приведения
выражение-pm ->* выражение-приведения
выражение-приведения:
унарное-выражение
( имя-типа ) выражение-приведения
унарное-выражение:
постфиксное-выражение
++ унарное выражение
— унарное выражение
унарная-операция выражение-приведения
sizeof унарная-операция
sizeof ( имя-типа )
выражение-размещения
выражение-освобождения
унарная-операция: один из
* & + - ! ~
выражение-размещения:
::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 )
выражение-освобождения:
::opt delete выражение-приведения
::opt delete [] выражение-приведения
постфиксное-выражение:
первичное-выражение
постфиксное-выражение [ выражение ]
постфиксное-выражение ( список-выражений opt )
имя-простого-типа ( список-выражений opt )
постфиксное-выражение . имя
постфиксное-выражение -> имя
постфиксное-выражение ++
постфиксное-выражение --
список-выражений:
выражение-присваивания
список-выражений , выражение-присваивания
первичное-выражение:
литерал
this
:: идентификатор
:: имя-функции-операции
:: уточненное-имя
( выражение )
имя
имя:
идентификатор
имя-функции-операции
имя-функции-преобразования
~имя-класса
уточненное-имя
уточненное-имя:
уточняющее-имя-класса :: имя
литерал:
целая константа
символьная константа
константа с плавающей точкой
строка литералов
R.17.3 Описания
описания:
спецификации-описания opt список-описателей opt ;
описание-asm
определение-функции
спецификация-связи
спецификация-описания:
спецификация-класса-памяти
спецификация-типа
спецификация-fct
спецификация-шаблона-типа
friend
typedef
спецификации-описания:
спецификации-описания opt спецификация-описания
спецификация-класса-памяти:
auto
register
static
extern
спецификация-fct:
inline
virtual
спецификация-типа:
имя-простого-типа
спецификация-класса
спецификация-перечисления
спецификация-сложного-типа
:: имя-класса
const
volatile
имя-простого-типа:
полное-имя-класса
уточненное-имя-типа
char
short
int
long
signed
unsigned
float
double
void
спецификация-сложного-типа:
служебное-слово-класса имя-класса
служебное-слово-класса идентификатор
служебное-слово-класса:
class
struct
union
уточненное-имя-типа:
имя-typedef
имя-класса :: уточненное-имя-типа
полное-имя-класса:
уточненное-имя-класса
:: уточненное-имя-класса
уточненное-имя-класса:
имя-класса
имя-класса :: уточненное-имя-класса
имя-перечисления:
идентификатор
спецификация-перечисления:
enum идентификатор opt { список-перечисления }
список-перечисления:
элемент-перечисления
список-перечисления , элемент-перечисления
элемент-перечисления:
идентификатор
идентификатор = выражение-константа
спецификация-связи:
extern строка-литерал { список-описаний opt }
extern строка-литерал описание
список-описаний:
описание
список-описаний описание
описание-asm:
asm ( строка-литерал) ;
R.17.4 Описатели
список-описаний:
описатель-с-инициализатором
список-описаний , описатель-с-инициализатором
описатель-с-инициализатором:
описатель инициализатор opt
описатель:
имя-в-описателе
операция-ptr описатель
описатель (список-описаний-параметров) список-спецификаций-cv opt
описатель [ выражение-константа opt]
( описатель )
операция-ptr:
* список-спецификаций-cv opt
& список-спецификаций-cv opt
полное-имя-класса :: * список-спецификаций-cv opt
список-спецификаций-cv:
const
volatile
имя-в-описателе:
имя
имя-класса
~имя-класса
имя-typedef
уточненное-имя-типа
имя-типа:
список-спецификаций-типа абстрактный-описатель opt
список-спецификаций-типа:
спецификация-типа список-спецификаций-типа
абстрактный-описатель:
операция-ptr абстрактный-описатель opt
абстрактный-описатель opt ( список-описаний-параметров ) список-спецификаций_cv opt
абстрактный-описатель opt [ выражение-константа opt ]
( абстрактный-описатель )
список-описаний-параметров:
список-описаний-парам opt ... opt
список-описаний-парам , ...
список-описаний-парам:
описание-параметра
список-описаний-парам , описание-параметра
описание-параметра:
спецификации-описания описатель
спецификации-описания описатель = выражение
спецификации-описания абстрактный-описатель opt
спецификации-описания абстрактный-описатель opt = выражение
определение-функции:
спецификации-описания opt описатель инициализатор-ctor тело-функции
тело-функции:
составной-оператор
инициализатор:
= выражение-присваивания
= { список-инициализаторов , opt }
( список-выражений )
список-инициализаторов:
выражение-присваивания
список-инициализаторов , выражение-присваивания
{ список-инициализаторов , opt }
R.17.5 Описания класса
спецификация-класса:
заголовок-класса { список-членов opt }
заголовок-класса:
служебное-слово-класса идентификатор opt спец-базовых opt
служебное-слово-класса имя-класса спец-базовых opt
служебное-слово-класса:
class
struct
union
список-членов:
описание-члена список-членов opt
спецификация-доступа : список-членов opt
описание-члена:
спецификации-описания opt список-описателей-членов opt ;
определение-функции ; opt
уточненное-имя ;
список-описателей-членов:
описатель-члена
список-описателей-членов , описатель-члена
описатель-члена:
описатель спецификация-чистой opt
идентификатор opt : выражение-константа
спецификация-чистой:
= 0
список-базовых:
спецификация-базовых
список-базовых , спецификация-базовых
спецификация-базовых:
полное-имя-класса
virtual спецификация-доступа opt полное-имя-класса
спецификация-доступа virtual opt полное-имя-класса
спецификация-доступа:
private
protected
public
имя-функции-преобразования:
operator имя-типа-преобразования
имя-типа-преобразования:
список-спецификаций-типа операция-ptr opt
инициализатор-ctor:
: список-инициализаторов-членов
список-инициализаторов-членов:
инициализатор-члена
инициализатор-члена , список-инициализаторов-члена
инициализатор-члена:
полное-имя-класса ( список-выражений opt )
идентификатор
имя-функции-оператор:
operator операция
операция: один из
new delete
+ - * / % ^ & | ~
! = < > += -= *= /= %=
^= &= |= << >> >>= <<= == !=
<= >= && || ++ — , ->* ->
() []
R.17.6 Операторы
оператор:
помеченный-оператор
оператор-выражение
составной-оператор
выбирающий-оператор
оператор-цикла
оператор-перехода
оператор-описания
помеченный-оператор:
идентификатор : оператор
case выражение-константа : оператор
default : оператор
оператор-выражение:
выражение opt ;
составной-оператор:
{ список-операторов opt }
список-операторов:
оператор
список-операторов оператор
выбирающий-оператор:
if ( выражение ) оператор
if ( выражение ) оператор else оператор
switch ( выражение ) оператор
оператор-цикла:
while ( выражение ) оператор
do оператор while (выражение)
for ( оператор-иниц выражение opt ; выражение opt ) оператор
оператор-иниц:
оператор-выражение
оператор-описание
оператор-перехода:
break ;
continue ;
return выражение opt ;
goto идентификатор ;
оператор-описания:
описание
R.17.7 Препроцессор
#define идентификатор строка-лексем
#define идентификатор ( идентификатор , ... , идентификатор ) строка-лексем
#include "имяфайла"
#include <имяфайла>
#line константа "имяфайла" opt
#undef идентификатор
условное:
часть-if части-elif opt часть-else opt строка-endif
часть-if:
строка-if текст
строка-if:
# if выражение-константа
# ifdef идентификатор
# ifndef идентификатор
части-elif:
строка-elif текст
части-elif строка-elif текст
строка-elif:
# elif выражение-константа
часть-else:
строка-else текст
строка-else:
# else
строка-endif:
# endif
R.17.8 Шаблоны типа
описание-шаблона-типа:
template < список-параметров-шаблона-типа> описание
список-параметров-шаблона-типа:
параметр-шаблона-типа
список-параметров-шаблона-типа , параметр-шаблона-типа
параметр-шаблона-типа:
параметр-типа
описание-параметра
параметр-типа:
class идентификатор
имя-шаблонного-класса:
имя-шаблона-типа < список-парам-шаблона-типа >
список-парам-шаблона-типа:
парам-шаблона-типа
список-парам-шаблона-типа , парам-шаблона-типа
парам-шаблона:
выражение
имя-типа
R.17.9 Обработка особых ситуаций
проверяемый-блок:
try составной-оператор список-обработчиков
список-обработчиков:
обработчик список-обработчиков opt
обработчик:
catch ( описание-особой-ситуации ) составной-оператор
описание-особой-ситуации:
список-спецификаций-типа описатель
список-спецификаций-типа абстрактный-описатель
список-спецификаций-типа
...
выражение-запуска:
throw выражение opt
спецификация-особой-ситуации:
throw ( список-типа opt )
список-типа:
имя-типа
список-типа , имя-типа
R.18 Приложение B: Совместимость
Это приложение не относится к справочному руководству С++ и не
является определением конструкций языка.
Язык С++ основывается на С (описание в книге Кернигана и Ритчи,
78 г., дальше K&R) и включает большинство изменений, предложенных
в ANSI стандарте для С. При конвертировании программ на языках
С++, K&R C и ANSI C могут возникнуть трудности в связи с различным
вычислением в них выражений. Транслятор должен распознавать все различия
между С++ и ANSI C. Программы на С++ и ANSI C должны иметь одинаковый
смысл за исключением трех следующих случаев:
В языке С выражение sizeof('a') равно sizeof(int), а в С++
оно равно sizeof(char).
Если есть описание
enum e { A };
то sizeof(A) равно в С sizeof(int), тогда как в С++ оно равно sizeof(e)
и не обязано быть равно sizeof(int).
Имя структуры, описанной во внутреннем блоке, может скрывать имя
объекта, функции, элемента перечисления или типа из внешнего блока.
Приведем пример:
int x[99];
void f()
{
struct x { int a; };
sizeof(x); /* для C это размер массива */
/* а для C++ размер структуры */
}
R.18.1 Расширения
В этом разделе перечисляются основные расширения языка С, введенные
в С++.
R.18.1.1 Возможности С++, введенные в 1985 г.
Здесь перечисляются возможности, добавленные к С, версией языка
С++ 1985 г.
Можно указывать типы формальных параметров функции ($$R.8.2.5), и они
будут проверяться ($$R.5.2.2). Будет происходить преобразование
типа ($$R.5.2.2). Это есть и в ANSI C.
В выражениях со значениями типа float вычисления могут проходить
с обычной точностью ($$R.3.6.1 и $$R.4.3). Это есть и в ANSI C.
Можно перегружать имена функций; $$R.13.
Можно перегружать операции; $$R.13.4
Возможна реализация вызова функций подстановкой; $$R.7.1.2.
Можно описывать объекты, представляющие данные, со спецификацией
const; $$R.7.1.6. Это есть и в ANSI C.
Можно описывать типа ссылки; $$R.8.2.2 и $$R.8.4.3.
Возможно управление свободной памятью с помощью операций new и
delete; $$R.5.3.3 и $$R.5.3.4.
Введены классы, которые позволяют: скрывать информацию ($$R.11),
проводить инициализацию ($$R.12.1), осуществлять пользовательские
преобразования типа ($$R.12.3) и работать с динамическими типами
с помощью виртуальных функций ($$R.10.2).
Имя класса или перечисления считается именем типа; $$R.9.
Указатель на любой объект c типом, не являющимся const или volatile,
можно присвоить указателю типа void*. Это есть и в ANSI C.
Указатель на функцию можно присваивать указателю типа void*;
$$R.4.6.
Описание внутри блока считается оператором; $$R.6.7.
Можно описывать безымянные объединения; $$R.9.5.
R.18.1.2 Возможности, добавленные в С++ после 1985 г.
Здесь перечисляются основные расширения С++ после 1985 г.:
Класс может иметь более одного прямого базового класса
(множественное наследование); $$R.10.1.
Члены класса могут быть защищенными; $$R.11.
Операции new и delete можно описывать в классе и перегружать;
$$r.5.3.3, $$R.5.3.4, $$R.12.5. Это позволило определенный способ
управления памятью для класса с помощью "присваивания указателю
this" отнести в раздел анахронизмов; $$R.18.3.3.
Можно явно уничтожать объекты; $$R.12.4.
Присваивания и инициализация для класса определены как присваивание и
инициализация по членам; $$R.12.8.
Служебное слово overload стало излишним и отнесено к разделу
анахронизмов; $$R.18.3.
Произвольные выражения разрешены в качестве инициализаторов статических
объектов; $$R.8.4.
Объекты, представляющие данные, могут быть volatile; $$R.7.1.6.
Также и в ANSI C.
Допустимы инициализаторы для статических членов класса; $$R.9.4.
Функции-члены могут быть статическими; $$R.9.4.
Функции-члены могут быть const или volatile; $$R.9.3.1.
Можно явно указать связывание с подпрограммами на других языках;
$$R.7.4.
Можно перегружать операции ->, ->* и ` ; $$R.13.4.
Классы могут быть абстрактными; $$R....


