15

如果我写,我可以在评估之前f(x)->g(args, ...)依赖一个序列点吗?我可以看到两种方式的论点:f(x)args, ...

  • §1.9.17 “在调用函数时(无论该函数是否内联),在函数体中的任何表达式或语句执行之前发生的所有函数参数(如果有)的评估之后都有一个序列点。在复制返回值之后和函数外的任何表达式执行之前还有一个序列点。
  • 另一方面,对象指针隐含地是一个隐藏的参数this,就好像我写g(f(x), args, ...)的那样,这表明它就像一个参数,因此未指定。

->运算符不是普通的二元运算符,因为显然g(...) 无法f(x)像我写的那样进行评估f(x) + g(...)。我很惊讶我找不到一些关于它的具体陈述。

4

2 回答 2

13

答案取决于您使用的是哪个版本的 C++ 标准(或您的编译器正在使用)。

C++ 2003 5.2.2 p8 说:

参数的评估顺序未指定。参数表达式评估的所有副作用在输入函数之前生效。后缀表达式和参数表达式列表的求值顺序未指定。

这意味着在评估和之间没有序列点。f(x)args

在 C++ 2011 中,序列点的整个概念已被替换(参见N1944),该措辞现在只是一个注释:

[注意:后缀表达式和参数表达式的计算相对于彼此都是无序的。参数表达式求值的所有副作用都在输入函数之前排序(见 1.9)。——尾注]

和 1.9 p15 说

调用函数时(无论该函数是否内联),与任何参数表达式或与指定被调用函数的后缀表达式相关的每个值计算和副作用都在执行主体中的每个表达式或语句之前进行排序称为函数。[注意:与不同参数表达式相关的值计算和副作用是无序的。——尾注]

这表示表达式f(x)和表达式args在 的主体中的所有内容之前排序g,但它们相对于彼此是无序的,这与 C++03 规则相同,但措辞不同。

C++14 与 C++11 具有相同的规则,但正如下面评论中所指出的,C++17 中的规则发生了变化。

C++ 2017 8.2.2 [expr.call] p5 说:

后缀表达式在表达式列表中的每个表达式和任何默认参数之前排序。参数的初始化,包括每个相关的值计算和副作用,相对于任何其他参数的初始化是不确定的。

这意味着对于您的示例,将按顺序执行以下步骤:

  • f被评估。
  • x被评估并且参数f被初始化。
  • f(x)评估函数调用。
  • f(x)->g被评估。
  • args并且其他参数g被评估并且参数g被初始化(以未指定的顺序)。
  • 最后,f(x)->g(args, ...)评估函数调用。
于 2013-03-01T22:36:53.893 回答
4

请注意,我认为您在标题中提出了一个问题,而在问题正文中提出了另一个问题。

嗯,其实并不矛盾。要评估您的功能,必须发生以下事情(不一定按此顺序)。

  • x 被评估 (A)
  • args 被评估 (B)
  • ... 被评估 (C)
  • f(x) 称为 (D)
  • 复制 f(x) 的返回值 (E)
  • return->g(args, ...) 被称为 (F)

现在,您引用的规则表明

  1. (A) 必须在 (D) 之前发生,因为在评估之前有一个评估函数参数的顺序点。
  2. (D) 发生在 (E) 之前,因为在函数运行之前无法进行复制。
  3. (F) 发生在 (E) 之后,因为调用 g(args) * 需要隐式指针
  4. (B) 和 (C) 发生在 (F) 之前,因为它们是论据。

但是,未排序的是(A),(B)和(C)之间的关系,或者在您的问题中(B)和(C)和(D)之间的关系,因为它们不是(F)的论据,他们可以事后评估。或者,可以事先评估它们。

* 有趣的问题。如果 g(args, ...) 是静态成员函数会发生什么。在这种情况下,由于从 f(x) 返回的指针实际上并没有传入,所以可以更早地对其进行排序吗?但这是一个单独的问题。

于 2013-03-01T22:00:30.600 回答