c++parameter-passingpass-by-name

Call by name (normal order evaluation)


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


Solution

  • 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.