函数的评估可以在编译期间发生吗?
Can evaluation of functions happen during compile time?
考虑下面的函数,
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)
到
int x = 15; // ... (1)
通过在编译时评估函数调用,因为函数的输入在编译时可用?
我知道在标记为 (2)
的通话中这是不可能的,因为输入仅在 运行 时间可用。
我不想知道使用任何特定语言的方法。我想知道为什么这可以或不能是编译器本身的一个特性。
C++ 确实有一个方法:
仔细阅读 C++11 中的 'constexpr' 关键字,它允许对函数进行编译时评估。
它们有一个限制:函数必须是一个return语句(不是多行代码),但可以调用其他constexpr
函数(C++14没有这个限制AFAIK ).
static constexpr int foo(int x){
return x + 5;
}
编辑:
为什么编译器可能不评估函数(只是我的猜测):
在没有被告知的情况下通过计算函数来删除函数可能不合适。
该函数可以在不同的编译单元中使用,并带有 static/dynamic 输入:因此在某些情况下对其进行评估并在其他地方添加调用。
这种使用会提供不一致的执行时间(尤其是在像 AVR 这样的确定性平台上),其中时间可能很重要,或者至少需要是可预测的。
中断(以及编译器如何与它们交互)也可能在这里发挥作用。
编辑:
constexpr is actually stronger -- it requires that the compiler do this. The compiler is free to fold away functions without constexpr, but the programmer can't rely on it doing so.
Can you give an example in the case where the user would have benefited from this but the compiler chose not to do it ?
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
是编译时常量,否则上面的代码将无法编译。
即使只是一个函数,内联函数也不能保证。并且链接器还可以在编译语法后对多个编译单元进行内联(检查整数常量的数组边界)。
这有点像 meta-programming,但没有模板。当然,这些示例并不能很好地说明主题,但是非常复杂的解决方案将受益于使用对象和函数式编程来实现结果的能力。
是的,评估可以在编译时发生。这属于常量折叠和函数内联的标题,这两者都是优化编译器的常见优化。
许多语言在 "compile time" 和 "run time" 之间没有明显的区别,但一般规则是该语言定义了一个 "execution model",它定义了任何特定程序的行为特定输入(或指定它是未定义的)。编译器必须生成一个可执行文件,该文件可以读取任何输入并生成执行模型定义的相应输出。 可执行文件内部发生了什么并不重要——只要从外部观察到的行为是正确的即可。
此处 "input"、"output" 和 "behavior" 包括执行模型中定义的与环境的所有可能交互,包括时间效应。
考虑下面的函数,
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)
到
int x = 15; // ... (1)
通过在编译时评估函数调用,因为函数的输入在编译时可用?
我知道在标记为 (2)
的通话中这是不可能的,因为输入仅在 运行 时间可用。
我不想知道使用任何特定语言的方法。我想知道为什么这可以或不能是编译器本身的一个特性。
C++ 确实有一个方法:
仔细阅读 C++11 中的 'constexpr' 关键字,它允许对函数进行编译时评估。
它们有一个限制:函数必须是一个return语句(不是多行代码),但可以调用其他constexpr
函数(C++14没有这个限制AFAIK ).
static constexpr int foo(int x){
return x + 5;
}
编辑: 为什么编译器可能不评估函数(只是我的猜测):
在没有被告知的情况下通过计算函数来删除函数可能不合适。
该函数可以在不同的编译单元中使用,并带有 static/dynamic 输入:因此在某些情况下对其进行评估并在其他地方添加调用。
这种使用会提供不一致的执行时间(尤其是在像 AVR 这样的确定性平台上),其中时间可能很重要,或者至少需要是可预测的。
中断(以及编译器如何与它们交互)也可能在这里发挥作用。
编辑:
constexpr is actually stronger -- it requires that the compiler do this. The compiler is free to fold away functions without constexpr, but the programmer can't rely on it doing so.
Can you give an example in the case where the user would have benefited from this but the compiler chose not to do it ?
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
是编译时常量,否则上面的代码将无法编译。
即使只是一个函数,内联函数也不能保证。并且链接器还可以在编译语法后对多个编译单元进行内联(检查整数常量的数组边界)。
这有点像 meta-programming,但没有模板。当然,这些示例并不能很好地说明主题,但是非常复杂的解决方案将受益于使用对象和函数式编程来实现结果的能力。
是的,评估可以在编译时发生。这属于常量折叠和函数内联的标题,这两者都是优化编译器的常见优化。
许多语言在 "compile time" 和 "run time" 之间没有明显的区别,但一般规则是该语言定义了一个 "execution model",它定义了任何特定程序的行为特定输入(或指定它是未定义的)。编译器必须生成一个可执行文件,该文件可以读取任何输入并生成执行模型定义的相应输出。 可执行文件内部发生了什么并不重要——只要从外部观察到的行为是正确的即可。
此处 "input"、"output" 和 "behavior" 包括执行模型中定义的与环境的所有可能交互,包括时间效应。