函数参数的破坏顺序是什么?

What is the order of destruction of function arguments?

如果调用函数 f 的参数 p_1, ..., p_n 类型分别为 T_1, ..., T_n带有参数 a_1, ..., a_n 并且它的主体抛出异常,完成或 returns,参数销毁的顺序是什么?为什么?如果可能,请提供标准参考。

编辑: 其实我想问的是函数 "parameters",但是 T.C。 Columbo 设法消除了我的困惑,我将这个问题留给论据并询问 。区别见这个问题的评论。

在 §5.2.2[4] N3337 is quite explicit on what happens (online draft):

During the initialization of a parameter, an implementation may avoid the construction of extra temporaries by combining the conversions on the associated argument and/or the construction of temporaries with the initialization of the parameter (see 12.2). The lifetime of a parameter ends when the function in which it is defined returns.

例如在

f(g(h()));

来自调用 h() 的 return 值是一个临时值,将在完整表达式结束时被销毁。然而,允许编译器避免这种临时性并直接用它的值初始化 g() 的参数。在这种情况下,return 值将在 g() return 秒后被销毁(即在调用 f() 之前)。

如果我正确理解了标准中的规定,但是不允许将 h() 中的值 return 保留到完整表达式的末尾,除非制作副本(参数)并且此副本被销毁一次 g() returns.

两种情况是:

  1. h return值用于直接初始化g参数。此对象在 g return 时和调用 f.
  2. 之前销毁
  3. h return 值是临时的。制作一个副本来初始化 g 参数,并在 g return 时销毁。原来的临时文件在完整表达式的末尾被销毁。

我不知道实施是否遵循了这方面的规则。

标准未指定计算函数参数的顺序。来自 C++11 标准 (online draft):

5.2.2 Function call

8 [ Note: The evaluations of the postfix expression and of the argument expressions are all unsequenced relative to one another. All side effects of argument expression evaluations are sequenced before the function is entered (see 1.9). —end note ]

因此,完全取决于实现来决定以什么顺序评估函数的参数。反过来,这意味着参数的构造顺序也取决于实现。

明智的实施方式是按照与构造相反的顺序销毁对象。

我没能在标准中找到答案,但我能够在 3 个最流行的 C++ 兼容编译器上测试它。 R Sahu 的回答很好地解释了它是实现定义的。

§5.2.2/8: The evaluations of the postfix expression and of the arguments are all unsequenced relative to one another. All side effects of argument evaluations are sequenced before the function is entered.

Visual Studio C++ 编译器 (Windows) 和 gcc (Debian)
参数的构造顺序与其声明相反,并以相反的顺序销毁(因此按声明顺序销毁):

2
1
-1
-2

Clang (FreeBSD)
参数按其声明的顺序构造并以相反的顺序销毁:

1
2
-2
-1

所有编译器都被指示将源代码视为 C++11,我使用以下代码片段来演示这种情况:

struct A
{
    A(int) { std::cout << "1" << std::endl; }
    ~A() { std::cout << "-1" << std::endl; }
};

struct B
{
    B(double) { std::cout << "2" << std::endl; }
    ~B() { std::cout << "-2" << std::endl; }
};

void f(A, B) { }

int main()
{
    f(4, 5.);
}