传递函数参数的求值顺序 - F1( int F2( int& x ), int x ) 的操作顺序

Order of Evaluation for passing function arguments - Order of operations for F1( int F2( int& x ), int x )

好的,我之前正在写一些代码。这是专门的行:

EnterNode( FindNode( terrain_X, terrain_Y, travel_dir ), travel_dir );

我在测试我的程序后注意到发生了一些奇怪的事情。外部函数接收的值不是我检查堆栈时读取的值。

我做了一个示例程序: https://ideone.com/wNjJrE

#include <iostream>

int modifyRef(int& A)
{
    A = 0;
    std::cout << "\nint& A = 0";
    return A;
}

void TakeValues(int X, int Y)
{
    std::cout << "\nX = " << X;
    std::cout << "\nY = " << Y;
}

int main()
{
    int Q = 9;
    TakeValues(modifyRef(Q), Q);
    std::cout << std::endl;
    system("pause");
    return 0;
}

这是我收到的输出:

int& A = 0
X = 0
Y = 9

我希望 Y 也为 0。如何定义参数绑定到函数调用的操作顺序?

(如果我使用的术语不正确,我深表歉意。)

函数参数的计算顺序是未指定。当你写:

TakeValues(modifyRef(Q), Q);

你依赖于 modifyRef(Q)Q 之前被评估的事实。但是函数参数的评估顺序是未指定的 - modifyRef(Q) 不一定排在 Q 之前,反之亦然。

在这种情况下,Q(第二个参数)首先被评估。所以我们读取9,并用它初始化参数Y然后我们评估modifyRef(Q),这将Q和returns归零,这导致用0初始化X .

尝试更改函数的签名:

int ModifyRef( int& A );

void TakeValues( int X, int Y );

以下...

int& ModifyRef( int& A );

void TakeValues( int& X, int& Y );

看看调用这行代码时 main 中的输出是什么:

int q = 9;

TakeValues( ModifyRef(q), q );

然后将参数的顺序颠倒过来

TakeValues( q, ModifyRef(q) );

并比较结果。

当我在 Intel Core2 Quad Extreme 上使用 VS2015 社区在我的机器 Win 7 64 位上执行此操作时,在使用引用时两种情况下我得到的结果是相同的,我得到的输出是:

出于测试目的,我将 ModifyRef()A 的值从 0 更改为 7。

int& A = 7
X = 7
Y = 7

对于内部函数是第一个参数,独立变量是第二个参数,或者内部函数是第二个参数,独立变量是第一个参数的两种情况。

因为我收到了相同的结果,所以在我看来,编译器如何创建和处理堆栈指针,ModifyRef() 似乎首先被评估,一旦该函数完成,因为 q 现在被引用而不是堆栈副本,它被 ModifyRef 函数中设置的任何值覆盖。

我还稍微修改了你的 ModifyRef() 函数,这样我就可以看到它传入的参数是什么,而不是将数字硬编码到它的打印输出语句中。

int& ModifyRef( int& A ) {
    A = 7; // Changed Value From 0 To 7 For Testing Purposes
    std::cout << "n\int& A = " << A;
    return A;
}

然而,这种效果可能仅在仅使用引用时适用。

当我恢复到您的原始函数签名并按以下顺序调用它时:

q = 9;
TakeValues( ModifyRef( q ), q );

正如您提供的原始代码一样,我得到的输出是:

int& A = 7
X = 7
Y = 9

但是当我将参数反转为:

q = 9;
TakeValues( q, ModifyRef( q ) );

我的输出是:

int& A = 7
X = 7
Y = 7

所以我在这两种情况下看到的情况略有不同。

在参数的第一个顺序中,Y或第二个参数被设置为9,因为q被初始化为9并且TakeValues()中的Y正在打印出一个值为 9 的堆栈副本。然后 XModifyRef() 计算,其中 q 的值为 9,但随后它被修改,因为它是一个引用,所以当 TakeValues()q 设置 Xq 已经从 9 更改为 7,因此 X 现在设置为 7。

在参数的第二个顺序中,似乎首先调用 ModifyRef() 并将 q 从 9 更改为 7 因此 TakeValues()Y 设置为 7 并且由于此函数使用引用 q 也从 9 更改为 7,因此当第一个参数使用 q 设置 Xq 已从 9 更改为7.

我不知道这是否依赖于编译器,但至少在我的机器上,参数的调用堆栈似乎是从最右边到左边发生的。当您考虑到函数何时具有默认值时,这也是有道理的。

示例:

class foo {
    void bar( int a, int b, int c = 3 ) {
        std::cout << a << ", " << b << ", " << c << std::endl;
    }
};

函数声明中的所有默认值都必须在最右边,因为您不能:

class foo {
   void bar( int a = 1, int b, int c ) { ... } // Compile Error
};

我希望这有助于阐明当您开始在函数中调用函数作为参数时代码中发生的事情,无论是使用按值传递(堆栈复制)还是按引用传递。