Это вообще хорошая идея для компилятора / интерпретатора для создания функций при интерпретации?

Когда я впервые изучал C ++, я заметил, что функции создаются сверху вниз, в отличие от языков, таких как Java, где порядок «объявлений» функции в коде не имеет значения.

C ++ пример:

#include <iostream>
using namespace std;

int main() {
test();
return 0;
}

void test() {

}

Но когда вы меняете порядок функций, программа работает нормально.

Было ли это преднамеренным, когда был разработан C ++?

0

Решение

Ни C, ни C ++ не требуют много порядка порядка определений функций по сравнению с использованием.

C позволяет объявлять функцию перед использованием, но (в C89 / 90) фактически не требует этого. Если был сделан вызов функции, которая не была объявлена, компилятор должен был сделать определенные предположения о типе функции (и код был недействительным, если определение функции не соответствовало этим предположениям).

С ++, однако, требует, чтобы свободные функции были, по крайней мере, объявленный перед использованием1. Определение функции также объявляет эту функцию, поэтому крошечные программы часто пишутся с определениями, предшествующими использованию, чтобы избежать необходимости писать объявление отдельно от их определений.

Для членов класса C ++ несколько ослабляет ограничения. Например, это вполне приемлемо:

class Foo {
void bar() { baz(); }
void baz() {}
};

Java отличается прежде всего простым запретом всех свободных функций, поэтому в нем есть только функции-члены, которые следуют примерно тем же правилам, что и функции-члены C ++.


  1. Без этого было бы практически невозможно поддерживать некоторые функции C ++, такие как перегрузка функций.
4

Другие решения

Проблема возникает из ряда ограничений:

  • Семантика значения требует, чтобы для составления «вызова» вам были нужны параметры и размеры возвращаемых типов.
  • что знания должны быть доступны компилятору во время компиляции того же модуля перевода, но …
  • знание, полученное из определений, доступно только во время связывания, что является отдельным этапом, который происходит помимо компиляции.

Чтобы еще больше усложнить, грамматика C ++ меняет значение в зависимости от того, являются ли символы переменными или типами (поэтому без этого знания a<b>c даже невозможно понять, что это означает: выражение, включающее три переменные или объявление переменной типа, заданной экземпляром шаблона).

Определение функции может находиться в другом исходном файле, и компилятор — при компиляции первого — не имеет никакого доступа к нему и, следовательно, знать, насколько широким должно быть пространство, оставляемое в стеке для передачи параметров и возврата.

Ваш образец — в широком проекте — будет код как

//test.h
double test();

__

//test.cpp
#include "test.h" //required to check decl & def consistence
double test()
{ /*...*/ }

__

//main.cpp
#include "test.h" // required to know about test() parameters and return
int main()
{
double z = test();   //how does "=" translate? we need to know what test returns
}

Этот «проект» будет скомпилирован с использованием независимых шагов:

g++ -c main.cpp //on a program developer machine
g++ -c test.cpp //on a library developer machine
g++ main.o test.o -o yourprogram //on a "package distributor" machine

Ни один из этих шагов не может собрать «глобальные знания» обо всем «глобальные символы«и их» перевод «в то же время. Вот почему нам нужно заголовки транслировать декларации кому-либо, кто должен их использовать

(Обратите внимание, что у функций-членов нет этой проблемы, поскольку все они должны оставаться в одних и тех же скобках класса и, как следствие, предоставлены для размещения одной и той же единицы перевода)

Такие языки, как Java или python, не имеют этой проблемы, поскольку все модули (независимо от того, как они написаны и загружены) загружаются одним и тем же экземпляром языковой машины, что, будучи уникальным, может собрать все символы и связанные типы. ,

язык, подобный D (который похож на C ++ в смысле «отдельной компиляции»), обеспечивает независимость порядка между вещами, находящимися в одном модуле, но требует «импорта» модуля для вещей, поступающих из других модулей, и, фактически, выполняет два шага перевод, сначала собирая символы и типы, а затем делая переводы вызовов.

Вы можете увидеть другое описание этой проблемы Вот

1