Техника разбора C++ объявлений или покажите мне слона.
От: ZAMUNDA Земля для жалоб и предложений
Дата: 12.03.09 01:25
Оценка: 1 (1)
Блн, пока писал вопрос, походу во всём разобрался. :) Вы всё равно посмотрите пожалуйста: там мелкие вопросы всётаки остались, и я что-то неправильно понять всёравно мог. Плюс ко всему, для меня, до этого момента (надеюсь), этот раздел C++ был китайской грамотой; так что надеюсь я не один такой дурак и информация будет полезной. Ну и жалко просто.
Если баян — простите.
---

Господа хорошие, помогите познать технику разбора C++ объявлений объектов и псевдонимов. Есть где-нибудь описание этого искусства?
Страуструп кое-что прояснил в этом деле, но про технику ничего не сказал, отговорился описанием порождений грамматики. У Липпмана вообще только примеры.

Я вроде как понимаю это, но всё мне кажется что понимаю я так, как представляли слона двое слепых в известной притче.
Вот например, объявление переменной (как я понимаю): сначала надо смотреть на суффикс, он самый главный: <пустой> — объект обыкновенный один :) , [] — массив, (T) — функция, где T -> объявления_параметров, потом слева направо читать спецификаторы, имя типа и префиксы.

const int n;
n — "пустой" суффикс, объект "n"
теперь читаю слева направо
const — константный
int — типа int
Итого: константный объект n, типа int
Проверил: АГА!
таким образом, это равносильно
int const n;
Проверил: АГА!

int * ap[];
ap[] — суффикс [], массив имя "ap"
опять слева направо
int — типа int
* — указатель
Итого: массив указателей на int
Проверил: АГА!

const int * pc
pc — "пустой" суффикс, объект "pc"
слева направо
const — константный
int — типа int
* — указатель
Итого: константный указатель на int
Проверил: НЕА! Это указатель на константный int

Читаю Страуструпа, он просто говорит, что обявление это: "спецификатор, базовый тип, объявляющая часть", — а потом расписывает что есть что в объявляющей части:
* — указатель
*const — константный указатель (т.е. указатель, и адрес на который он указывает, изменить нельзя).
...

Значит надо разбить объявление на конструкции: "[<спецификатор>] <тип>", "*[<спецификатор>]", "&", "имя[<суффикс>]" а потом сначала смотреть, что говорит суффикс или его отсутствие, а затем слева направо читать префиксные конструкции.
таким образом:
int * ap[];
ap[] — суффикс [], массив имя "ap"
"int", "*", "ap[]" (это в первую очередь)
"ap[]" — массив, звать "ap"
"int" — тип int, без выкрутасов
* — указатель
Итого: массив указателей на int
Проверил: АГА!

const int an[];
Массив константных int.
Проверил:
const int an[] = {0,1,2};
an[1] = n; // Ошибка, константу нельзя менять.
const int an1[]; // Ошибка, не инициализированная константа.
АГА!

int & *const prn;
Константный указатель на ссылку на int. -- белеберда какая-то, но...
Проверил: АГА! Компилятор сказал: "error: cannot declare pointer to `int&'", -- т.е. та белеберда которую я и думал. :)

СТОП! Как же быть с
int const n;
, у Страуструпа сказано, что спецификатор может быть только перед базовым типом! Или Страуструп что-то не договорил, и "[<спецификатор>]<тип>" <==> "<тип>[<спецификатор>]"? Или это компилятор (MinGW 5.1.4) просто разрешает?

Ну ладно, теперь со скобками: я так понимаю что, как и в выражениях, префиксы внутри скобок рассматриваются раньше всего остального, т.е. суффиксов.

Например если я хочу объявить ссылку на массив, то без скобок не обойтись. Т.к. сдесь
int an[] = {0,1,2};
int &ran[] = an;
ran разберается так:
"int", "&", "ran[]" (первым)
"ran[]" — массив, звать "ran"
"&" — ссылка
"int" — тип int
Итого: массив ссылок на int.
Проверил: АГА!

Со скобками:
int an[] = {0,1,2};
int (&ran)[] = an;
ошибка!!!
???
Ай, я массив-то сделал int[3], компилятор ж по инициализатору обо всём дотумкал. Поправил:
int an[] = {0,1,2};
int (&ran)[3] = an;
работает, а разбирается так:
"int", "&ran" (первым), "[]"
"&ran" — ссылка, звать "ran"
"[3]" — массив о трёх элементах
"int" — тип int
Итого: ссылка на массив из трёх int.

Теперь попробую извратиться
int n = 99;
int (const*) cpn = &n;
, м.б. "(const*)" будет понято как "*const", но компилятор что-то страшное выдал. Значит скобки должны в себе содержать некую корректную "объявляющую часть".

Про запятые (т.е. конструкции типа 'int k, l, * pn') я понимаю что после запятой надо мысленно вставить базовый тип с его спецификаторами из первого объявления, чтоб получить то что подразумевается в том что после запятой.
int n, * pn; 
// равносильно
int n; int * pn;

const int cn, & rcn = cn;
// равносильно
const int cn; const int & rcn = cn;
Проверил: АГА! Всё сходится.
Наука изощряет ум; ученье вострит память.
(c) Козьма Прутков
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.