一般来说,C++ 不使用名称调用。按名称调用意味着函数的参数不会在函数调用时进行评估。它的行为就像参数将被替换到函数体中一样。
例子:
void foo(int a, int b) {
int s = 0;
for(int i=0; i<n; i++) {
s += b;
}
}
通常在 C++ 中,一个示例表达式的x = foo(3,bar(7));
行为类似于:
int tmp = bar(7);
x = foo(3,tmp);
bar
被评估一次并将结果提供给函数foo
.
使用名称调用的语言可能会转换foo(3,bar(7));
为
void foo() {
int s = 0;
for(int i=0; i<3; i++) {
s += bar(7);
}
}
在第一种情况下,函数bar
将被评估一次,在第二种情况下,它将被评估3
次数。
但是,该规则也有例外。当函数声明已知时(例如对于模板和内联),优化器可以使用它来生成优化代码。
例子:
inline unsigned foo(unsigned a, unsigned b) {
return a / b;
}
如果您调用a = foo(x,2);
编译器将足够聪明,可以将其转换为a = x/2;
然后再转换为a = x >> 1;
.
这甚至到了这个例子:
inline int foo(int a, int b) {
if(a == 0) return 0;
else return b;
}
现在编译器确实可以转换x = foo(0,bar(17));
为x = 0;
从不调用该函数bar
。我认为只有在确保bar
没有副作用的情况下才进行这种优化。
虽然 C++ 不使用名称调用,但您可以轻松地在 C++ 中使用这一理念。只需给你的函数一个函数对象/指针。
例子:
template<typename F>
int foo(int a, F b) {
int s = 0;
for(int i=0; i<a; i++) {
s += b();
}
}
现在有了foo(3, []() { static int i=0; return i++; })
,其中第二个参数是 C++11 lambda,每次在代码中遇到 b 时都会对其进行评估。