编译器/解释器在解释时创建函数通常是个好主意吗?

Is it generally a good idea for a compiler/interpreter to create functions while interpreting?

本文关键字:好主意 函数 解释器 解释 创建 编译器      更新时间:2023-10-16

当我第一次学习c++时,我注意到函数是从上到下创建的,不像Java这样的语言,代码中函数"声明"的顺序无关紧要。

c++的例子:

#include <iostream>
using namespace std;
int main() {
    test();
    return 0;
}
void test() {
}

但是当你交换函数的顺序时,程序可以正常工作。

在设计c++时是有意的吗?

与用法相比,C和c++都没有对函数定义的顺序有太多的要求。

C允许在使用函数之前声明函数,但(在C89/90中)实际上并不需要这样做。如果调用了一个没有声明过的函数,编译器就需要对函数的类型做出一定的假设(如果函数的定义不符合这些假设,代码就无效)。

然而,c++要求自由函数在使用1之前至少要声明。函数定义也声明了该函数,因此小程序通常在使用之前使用定义来编写,以避免必须将声明与定义分开编写。

对于类成员,c++在一定程度上放宽了限制。例如,这是完全可以接受的:

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

Java的主要区别在于简单地禁止所有自由函数,因此它只有成员函数,这些成员函数遵循与c++成员函数大致相同的规则。


    如果没有这个,基本上不可能支持一些c++特性,比如函数重载。

问题源于一系列约束:

  • 值语义要求编译一个"调用",你需要知道参数和返回类型大小。
  • 编译器在编译同一翻译单元时必须能够获得这些知识,但是……
  • 来自定义的
  • 知识仅在链接时可用,这是在编译之外发生的单独步骤。

更复杂的是,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++)允许驻留在同一模块中的事物之间的顺序独立,但需要对来自其他模块的事物进行模块"导入",实际上-通过首先收集符号和类型然后进行调用翻译来进行两步翻译。

你可以在这里看到这个问题的另一个描述