返回对象的函数调用和该对象上的方法调用之间是否存在序列点?

时间:2013-03-01 21:08:04

标签: c++ sequence-points unspecified-behavior

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

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

->运算符不是普通的二元运算符,因为g(...) 无法在f(x)之前进行评估,就像我写f(x) + g(...)一样}。我很惊讶我找不到一些具体的说法。

2 个答案:

答案 0 :(得分:12)

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

C ++ 2003 5.2.2 p8说:

  

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

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

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

  

[注意:后缀表达式和参数表达式的评估都是相对于彼此无法排序的。在输入函数之前,对参数表达式评估的所有副作用进行排序(参见1.9)。 - 结束记录]

和1.9 p15说

  

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

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

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

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

  

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

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

    评估
  • f
  • 评估
  • x并初始化f的参数。
  • 评估函数调用f(x)
  • 评估
  • f(x)->g
  • argsg的其他参数被评估,g的参数被初始化(以未指定的顺序)。
  • 最后,评估函数调用f(x)->g(args, ...)

答案 1 :(得分: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. 所必需的
  5. (B)和(C)发生在(F)之前,因为它们是参数。
  6. 但是,未经测试的是(A),(B)和(C)之间的关系,或者(B)和(C)和(D)之间的关系,因为它们不是(() F),之后可以对它们进行评估。或者,他们可以事先进行评估。

    *有趣的问题。如果g(args,...)是静态成员函数会发生什么。在这种情况下,由于实际上没有传入来自f(x)的返回指针,它是否可以提前排序?但这是一个单独的问题。