Compilation 函数的求值可以在编译时进行吗?

Compilation 函数的求值可以在编译时进行吗?,compilation,language-agnostic,compiler-optimization,Compilation,Language Agnostic,Compiler Optimization,考虑下面的函数 public static int foo(int x){ return x + 5; } 现在,让我们称之为 int in = /*Input taken from the user*/; int x = foo(10); // ... (1) int y = foo(in); // ... (2) 在这里,编译器可以更改吗 int x = foo(10); // ... (1) 到 因为函数的输入在编译时可用,所以在编译时评估函数调用

考虑下面的函数

public static int foo(int x){
     return x + 5;
}
现在,让我们称之为

int in = /*Input taken from the user*/;
int x = foo(10);     // ... (1)
int y = foo(in);     // ... (2)
在这里,编译器可以更改吗

int x = foo(10);     // ... (1)

因为函数的输入在编译时可用,所以在编译时评估函数调用

我知道这在标记为
(2)
的调用期间是不可能的,因为输入仅在运行时可用


我不想知道用任何特定的语言做这件事的方法。我想知道为什么这可以或不可以是编译器本身的一项功能。

C++确实有一种方法可以做到这一点:

阅读C++11中的“constexpr”关键字,它允许对函数进行编译时计算

它们有一个限制:函数必须是return语句(不是多行代码),但可以调用其他
constepr
函数(C++14没有这个限制)

编辑: 为什么编译器可能不计算函数(我猜是这样):

在没有被告知的情况下通过计算函数来删除函数可能是不合适的

该函数可以在不同的编译单元中使用,也可以与静态/动态输入一起使用:因此在某些情况下对其进行评估,并在其他地方添加调用

这种使用将提供不一致的执行时间(特别是在AVR这样的确定性平台上),其中时间可能很重要,或者至少需要是可预测的

中断(以及编译器如何与它们交互)也可能在这里发挥作用

编辑:

constexpr实际上更强大——它要求编译器这样做。编译器可以在没有constexpr的情况下自由地折叠函数,但程序员不能依赖它这样做


你能举一个例子吗?如果用户本可以从中受益,但编译器选择不这么做

inline
函数可以解析为常量表达式,也可以不解析为常量表达式,这些常量表达式可以优化为最终结果

但是,
constexpr
保证了这一点。内联函数不能用作编译时常量,而
constexpr
可以让您制定编译时函数以及更多对象

一个基本示例,其中
constexpr
保证内联不能

constexpr int foo( int a, int b, int c ){
  return a+b+c;
}

int array[ foo(1, 2, 3) ];
和一个简单的物体一样

struct Foo{
  constexpr Foo( int a, int b, int c ) : val(a+b+c){}
  int val;
};

constexpr Foo foo( 1,2,4 );

int array[ foo.val ];
除非
foo.val
是编译时常量,否则上述代码不会编译

即使只是一个函数,内联函数也不能保证。并且链接器还可以在编译语法(检查整数常量的数组边界)后在多个编译单元上进行内联


这有点像元编程,但没有模板。当然,这些示例并不能很好地解释主题,但是非常复杂的解决方案将受益于使用对象和函数编程来实现结果的能力。

是的,可以在编译时进行评估。这属于常量折叠和函数内联的标题,它们都是优化编译器的常用优化

许多语言在“编译时”和“运行时”之间没有很强的区别,但一般规则是该语言定义了一个“执行模型”,该模型定义了具有任何特定输入的任何特定程序的行为(或指定它是未定义的)。编译器必须生成一个可执行文件,该文件可以读取任何输入并生成执行模型定义的相应输出。可执行文件内部发生的事情并不重要——只要外部查看的行为是正确的


此处的“输入”、“输出”和“行为”包括与执行模型中定义的环境的所有可能交互,包括计时效果。

您可能会在cxx11中找到
constepr
说明符。@John3136谢谢!非常感谢。我明白,但这个问题并不是专门针对任何语言的。我的意思是问为什么编译器自己能或不能完成它?好吧,我已经添加了一个编辑,但这只是我的观点。
在没有被告知的情况下通过计算函数来删除函数可能不合适。
(假设“remove”的意思是删除函数调用,而不是函数本身)-嗯,这就是函数内联时发生的情况,对吗?内联函数是一种非常常见的优化方法。
constexpr
实际上更强大——它要求编译器这样做。编译器可以自由折叠函数,而不必使用
constexpr
,但程序员不能依赖它这样做。@ChrisDodd您能举一个例子,说明用户本可以从中受益,但编译器选择不这样做吗?
constexpr int foo( int a, int b, int c ){
  return a+b+c;
}

int array[ foo(1, 2, 3) ];
struct Foo{
  constexpr Foo( int a, int b, int c ) : val(a+b+c){}
  int val;
};

constexpr Foo foo( 1,2,4 );

int array[ foo.val ];