I have the following code below (C like language). I know how to trace if pass-by-value or pass-by-reference was used, but there is also call-by-name mechanism. Can anyone guide me how to trace call-by-name?
int x=12,y=10;
void tswap(int pa, int pb) {
int tmp;
tmp=pa;
pa=pb;
pb=tmp;
x=x+pa;
x=x-pb;
y++;
printf("%d %d %d %d\n",pa,pb,x,y);
}
int main() {
int a=4;
tswap(x,a);
printf("%d %d %d\n",x,y,a);
tswap(++x,++y);
printf("%d %d %d\n",x,y,a);
return 0;
}
If pass-by-value would be used that output would look like:
4 12 4 11
4 11 4
12 5 12 13
12 13 4
In general C++ does not use call-by-name. Call-by-name means that the argument to the function is not evaluated on function call. It behaves as if the argument would be substituted into the function body.
Example:
void foo(int a, int b) {
int s = 0;
for(int i=0; i<n; i++) {
s += b;
}
}
Normally in C++ an example expression like x = foo(3,bar(7));
would behave similar to this:
int tmp = bar(7);
x = foo(3,tmp);
bar
is evaluated once and the result given to the function foo
.
A language which would use call-by-name would potentially transform foo(3,bar(7));
to
void foo() {
int s = 0;
for(int i=0; i<3; i++) {
s += bar(7);
}
}
In the first case the function bar
would be evaluated once, in the second case it would be evaluated 3
times.
However there are exceptions to that rule. When the function declaration is known (e.g. for templates and inlines) the optimizer may use it to generate optimized code.
Example:
inline unsigned foo(unsigned a, unsigned b) {
return a / b;
}
If you call a = foo(x,2);
the compiler would be smart enough to translate that to a = x/2;
and then to a = x >> 1;
.
This even goes as far as this example:
inline int foo(int a, int b) {
if(a == 0) return 0;
else return b;
}
Now the compiler can indeed transform x = foo(0,bar(17));
to x = 0;
thus never calling the function bar
. I think this optimization is only done when it is assured that bar
has not side-effects.
While C++ does not use call-by-name you can easily use the ideom in C++. Just give a function object/pointer to your function.
Example:
template<typename F>
int foo(int a, F b) {
int s = 0;
for(int i=0; i<a; i++) {
s += b();
}
}
Now with foo(3, []() { static int i=0; return i++; })
, where the second argument is a C++11 lambda, b would be evaluated each time it is encountered in you code.