【问题标题】:In what order does evaluation of post-increment operator happen?后增量运算符的评估以什么顺序发生?
【发布时间】:2010-03-29 14:20:00
【问题描述】:

给定

std::vector<CMyClass> objects;
CMyClass list[MAX_OBJECT_COUNT];

这样做是否明智?

for(unsigned int i = 0; i < objects.size(); list[i] = objects.at(i++));

或者我应该把我的循环扩展到这个吗?

for(unsigned int i = 0; i < objects.size(); i++)
{
  list[i] = objects.at(i);
}

【问题讨论】:

  • listobjects 有哪些类型?
  • 我更新了我的问题以反映这一点。

标签: c++ stl operator-precedence


【解决方案1】:

前者是未定义的行为。未指定在函数调用 objects.at 之前还是之后评估 list[i](为赋值的 lhs 提供左值)。

因此,表达式的各个部分有一个合​​法的顺序,其中i 被访问(在list[i])和单独修改(在i++),没有中间序列点。

这正是 C++ 标准中未定义行为的条件 - 是否存在这样的合法顺序。 IIRC C 标准的表达方式略有不同,但效果相同。

如果有疑问,不要编写使用自增运算符的表达式,并且在表达式的其他任何地方也使用相同的值。您可以使用逗号运算符(i++, i++ 很好)和条件运算符(i ? i++ : i-- 很好)来做到这一点,因为它们中有序列点,但这很少值得。 ||&amp;&amp; 同样,像 p != end_p &amp;&amp; *(p++) = something; 这样的东西并非完全不可信。任何其他用途,如果你盯着它看足够长的时间,你通常可以制定出一个把事情搞砸的评估顺序。

除了复杂的 for 表达式和带有空主体的 for 循环的可理解性之外。

【讨论】:

  • 现在,在前面提到的 for 循环中使用它们会很有趣。
  • @MSalters:坏点。它是未定义的。 5/4 说,“对于完整表达式的子表达式的每个允许排序,都应满足本段的要求,否则行为是 undefined ”(我的重点)。提问者的表达有一个允许的顺序,不满足要求,“只能访问先验值以确定要存储的值”。出于某种原因,该段中的示例说行为未指定,我猜在这些情况下单独使用i 作为左值不会不按顺序访问i
  • ".at(i++)i++ 之后有一个序列点"。为真,但序列点仅在使用objects.at(i++) 之前对子表达式i++objects 进行排序。它没有相对于i++ 对子表达式list[i] 进行排序,这是造成问题的原因。鼻恶魔啊。
  • 即使[] 是一个函数调用,我也不确定你是否得救了。可以首先评估子表达式i++,然后是list[i],然后是objects.at(),然后是operator=。这使得 i++ 被评估和 i 在没有中间序列点的情况下被访问。但我从来没有 100% 理解标准中“先验价值”的含义,而不是说“价值”。可能没有访问“先前值”,而是访问了其他一些值。我认为但不确定list[i]i 是一个子表达式(因此可以在i++ 之前评估,然后 访问先前的值)。
  • 无论如何,也许我对标准的这一部分过于偏执,在某些情况下,我会断言不存在的未定义行为,因为我认为对 i 的某些访问属于“先前的价值”,而事实上它不可能。我愿意在那个方向上犯错,而不是尝试任何真正的核心语言律师来证明(如果可以的话)为什么在评估i++ 之前不能立即访问i :-)
【解决方案2】:

如有疑问,请选择更易于理解的形式(展开循环)。

(而且我认为list[i] = objects.at(i++) 会导致未定义的行为。)

【讨论】:

    【解决方案3】:

    在与i++ 相同的表达式中引用i 可能是未定义的行为。但既然你看起来像在使用容器,你能不能写...

    list = objects;                               // if they're the same type
    list.assign(objects.begin(), objects.end());  // if not
    

    【讨论】:

    • 或者在最坏的情况下使用 std::copy 如果这些选项都不可用(比如说 list 是一个 C 数组?)。
    【解决方案4】:

    正如已经说过的,在使用它的同一表达式中对变量进行后递增会产生未定义的行为。 但是,如果您希望保持紧凑的形式,您可以引入一个序列点并使用

    for(unsigned int i = 0; i < objects.size(); list[i] = objects.at(i), i++);
    

    【讨论】:

    • 这看起来像旧的“每个程序都可以编码为无体 for 循环”。
    猜你喜欢
    • 2018-09-13
    • 2015-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-18
    • 2014-11-07
    • 2013-07-30
    相关资源
    最近更新 更多