@daniil14056

Тонкости Компиляторов. Почему в классах с++ не требуется объявление функции до вызова?

int sum(int,int);
int proc(int a,int b){
    return sum(a,b);
}
int sum(int a,int b){
    return a+b;
}

Почему в классах, это не требуется?

Вообще с++ компиляторы они однопроходные или двухпроходные? Если судить по правилам декларирования функций. То должны быть однопроходными. И AST дерево на лету как в книге Дракона пример можно писать.
Или синтактический анализ у класса по другому работает?

К примеру каким образом при синтактическом анализе можно определить приведения типов. Вообще там много примеров.
И для каждого из 3 примеров, еще условия
  1. Тип уже определен выше, и находится в таблице символов(такое решаемо)
  2. Тип не определен, и может быть ниже, как java c# jc, ( не знаю как решить)

(A)a
(A)*a -> например тут это умножение или приведение типа
(A*)++a; -> а вот тут как определить Это постфиксный или префиксный инкремент ++
((((((((((((A::B::C::D::L*)))))))))++ + ++( ... ); такой пример можно выдумать.

В вики есть статья Lexer Hack, но после слова неэлегантно я закрыл, слишком большая жертва.

У меня есть решение, но которое мне не нравится, и не нашел что бы так делали, это разбить весь файл на токены первым проходом. После этого пройтись по токенам, и конкретизировать их уже.
lexicalworking.jpg
К примеру что примеры кода, что статьи, везде такое вижу, что Парсер и Лексер работают одновременно в режиме, запрос-ответ как по картинке. А как тогда можно решить задачу, это идентификатор типа или переменной, если она определена ниже.
  • Вопрос задан
  • 294 просмотра
Пригласить эксперта
Ответы на вопрос 4
wataru
@wataru Куратор тега C++
Разработчик на С++, экс-олимпиадник.
Там явно больше одного прохода.

Правила видимости функций растут еще из Си. Не знаю конкретно о чем думали, но сделали точно также и в Си++. Когда стали вводить классы от этого наследия отказались, ведь в Си никаких классов и методов нет.
Ответ написан
Комментировать
@Mercury13
Программист на «си с крестами» и не только
Подозреваю, что объявление функции после вызова разрешили, потому что оно достаточно легко реализуемо, но даёт изрядную свободу прогеру. Например: несколько открытых конструкторов вызывают private init(), а этот init, как и все скрытые методы, сидит где-то в конце.

Проходов компиляции может быть много — например, сначала мы говорим, что f(x) — это вызов, а вторым проходом определяем, с какой функцией f имеем дело. К тому же конкретно в Си++ любой код в теле класса — автоматический inline, то есть не создаёт кода, пока кто-то снаружи не вызовет.

Умножение, приведение типа или ещё какая-то дрянь — это особенность метода разбора. Во многих языках, в том числе в Паскале, используется LL-разбор — сканирование слева направо, опознание слева направо. Ошибки наподобие «ожидалась точка с запятой» — это артефакт примитивных LL-анализаторов. В Си используется LR-разбор — сканирование слева направо, опознание справа налево, а ожидающие опознания лексемы хранятся в стеке.

И вообще, если пишете собственный крайне специализированный язык, делайте его LL(1), это очень упростит разбор.

Кстати, в Паскале принят обратный порядок видимости: private → protected → public → published. Последнее — поле доступно в рефлексии времени выполнения.
Ответ написан
Комментировать
mayton2019
@mayton2019
Bigdata Engineer
Конвейер компилляции С++ точно не прост. На хай-левеле это 3 шага. Препроцессор, компилляция и линкер.
А сама компилляция в свою очередь тоже сложна. Там полюбому будет работа с шаблонным процессором
и еще будет обязательно проход оптимизации. Это когда из кода можно выкинуть недостижимые ветки или
размотать циклы. И это тоже шаг.
Ответ написан
Комментировать
maaGames
@maaGames
Погроммирую программы
Думаю, основная разница в том, что методы класса гарантированно находятся в том же самом файле в пределах скобочек. А вот просто функции могут быть где угодно в других файлах, которые ещё не обработаны.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы