【问题标题】:Is there a sequence point between a function call returning an object and a method call on that object?在返回对象的函数调用和对该对象的方法调用之间是否存在序列点?
【发布时间】:2013-02-16 11:53:54
【问题描述】:

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

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

-> 运算符不是普通的二元运算符,因为很明显g(...) 不能f(x) 之前评估,就像我写f(x) + g(...) 一样。我很惊讶我找不到关于它的具体说明。

【问题讨论】:

  • -> 是二元运算符吗?如果忽略它的外观,它看起来就像一个返回指向某种类型的指针的一元运算符。
  • @Yakk:它是一个一元运算符,但 n-ary (某种意义上)是 T::g,它采用隐式 thisargs .问题是产生隐式 this 的表达式是否在评估成员函数 g 的其余参数之前排序
  • 看起来像it's not

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


【解决方案1】:

答案取决于您使用的 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 说:

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

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

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

【讨论】:

  • 这在 C++17 中有所改变:“14) 在函数调用表达式中,命名函数的表达式在每个参数表达式和每个默认参数之前排序。” en.cppreference.com/w/cpp/language/eval_order 所以现在,f(x) 将在 args 之前进行评估。
【解决方案2】:

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

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

  • 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) 返回的指针实际上并没有传入,所以可以更早地对其进行排序吗?但这是一个单独的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多