【问题标题】:Call by name (normal order evaluation)按名称调用(正常订单评估)
【发布时间】:2014-04-17 11:05:38
【问题描述】:

我在下面有以下代码(类似 C 的语言)。我知道如何跟踪是否使用了按值传递或按引用传递,但也有按名称调用机制。谁能指导我如何追踪姓名呼叫?

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;
}

如果使用按值传递,则输出将如下所示:

4 12 4 11

4 11 4

12 5 12 13

12 13 4

【问题讨论】:

  • C++ 没有按名称调用机制。 (您可以通过让函数采用函数对象来模拟它。)
  • 当我运行这个程序时,我准确地得到了你发布的输出。但是函数调用并没有什么特别之处:所有参数都是按值传递的。老实说,我从来没有听说过 C++ 中的 pass-by-name...
  • 传名真的不存在!它只是理论上的概念:)

标签: c++ parameter-passing pass-by-name


【解决方案1】:

一般来说,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 &gt;&gt; 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 时都会对其进行评估。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-06-03
    • 1970-01-01
    • 2014-09-15
    • 1970-01-01
    • 1970-01-01
    • 2013-07-24
    • 2011-06-05
    • 1970-01-01
    相关资源
    最近更新 更多