【问题标题】:Double assignment of the same variable in one expression in C++11在 C++11 中的一个表达式中对同一变量进行双重赋值
【发布时间】:2013-11-04 09:32:42
【问题描述】:

The C++11 standard (5.17, expr.ass) 声明

在所有情况下,赋值都是在值计算之后排序的 左右操作数,在计算值之前 赋值表达式。关于一个 不确定顺序的函数调用,复合的操作 分配是一个单一的评估

据我了解,作为给定赋值一部分的所有表达式都将在赋值本身之前进行评估。即使我在同一个赋值中修改同一个变量两次,这条规则也应该起作用,我相当肯定,以前是未定义的行为。

请问给定的代码:

int a = 0;
a = (a+=1) = 10;

if ( a == 10 ) {
    printf("this is defined");
} else {
    printf("undefined"); 
}

总是评估为a==10?

【问题讨论】:

  • 旁注:检查可能未定义的表达式的结果并不能告诉我们它是否实际上是 UB。它可以是 UB 并产生正确的结果。
  • @jrok 这是一个示例代码,甚至可能是一个 SSCCE,因为 SO 要求我包含有效代码。出于好奇,我确实尝试过测试它,但我意识到它证明不了任何事情;因此我什至没有提到它。
  • @jrok - 它可以产生“正确”的结果。引号很重要。 <g>

标签: c++ c++11 variable-assignment operator-precedence expression-evaluation


【解决方案1】:

是的,C++98 和 C++11 之间发生了变化。我相信您的示例在 C++11 规则下定义良好,而在 C++98 规则下表现出未定义的行为。

作为一个更简单的示例,x = ++x; 在 C++98 中未定义,但在 C++11 中已明确定义。请注意,x = x++; 仍然未定义(后自增的副作用与表达式的求值无关,而预自增的副作用在其之前排序)。

【讨论】:

  • 这很有趣并且很高兴知道,尽管很遗憾您没有解决发布的代码。您对它是未定义/未指定的行为有任何想法吗?
  • 我相信您的示例定义明确。相应地更新了答案。
【解决方案2】:

经过一些研究,我确信您的代码行为在 C++11 中得到了很好的定义。

$1.9/15 州:

运算符的操作数的值计算顺序在前面 算子结果的值计算。

$5.17/1 状态:

赋值运算符 (=) 和复合赋值运算符全部分组 从右到左。

如果我理解正确,在你的例子中

a = (a+=1) = 10;

这意味着(a+=1)10 的值计算必须在(a+=1) = 10 的值计算之前进行,并且该表达式的值计算必须在a = (a+=1) = 10; 被评估之前完成。

$5.17/1 状态:

在所有情况下,赋值顺序在左右操作数的值计算之后,赋值表达式的值计算之前。

这意味着赋值必须发生在值计算之前,因此,由于传递性,(a+=1) = 10 的评估只能在赋值 a+=1 之后开始(因为它的值只能在副作用之后计算) .

第二个和第三个作业也是如此。

另请参阅excellent answer,它比我能解释的更详细和更好地解释了先排序关系。

【讨论】:

    【解决方案3】:

    让我们将你的代码重写为

    E1 = (E2 = E3)
    

    其中 E1 是表达式 a,E2 是表达式 a += 1,E3 是表达式 10。在这里,我们使用了赋值运算符从右到左分组(C++11 标准中的第 5.17/1 节)。

    §5.17/1 还指出:

    在所有情况下,赋值顺序在左右操作数的值计算之后,赋值表达式的值计算之前。

    将此应用于我们的表达式意味着我们首先必须评估子表达式E1E2 = E3。请注意,这两个评估之间没有“sequenced-before”关系,但这不会导致任何问题。

    id-expression E1 的评估是微不足道的(结果是 a 本身)。 assignment-expression E2 = E3 的求值过程如下:

    首先必须计算两个子表达式。 literal E3 的评估又是微不足道的(给出的 prvalue 值为 10)。

    (复合)assignment-expression E2 的评估按以下步骤完成:

    1) a += 1 的行为等价于 a = a + 1a 只评估一次(第 5.17/7 节)。在评估子表达式a1(以任意顺序)后,将左值到右值 转换应用于a 以读取存储在a 中的值。

    2) 将a(即0)和1 的值相加(a + 1),此相加的结果是值1 的prvalue。

    3) 在我们计算赋值a = a + 1 的结果之前,左操作数所指的对象的值被右操作数的值替换(第 5.17/2 节)。然后E2 的结果是一个指向新值1 的左值。请注意,副作用(更新左操作数的值)在赋值表达式的值计算之前排序。这是上面引用的 §5.17/1。

    现在我们已经评估了子表达式E2E3,表达式E2所指的值被替换为E3的值,即10。因此E2 = E3 的结果是值10 的左值。

    最后,E1 引用的值表达式被替换为表达式E2 = E3 的值,我们计算为10。因此,变量a最终包含值10

    由于所有这些步骤都是明确定义的,因此整个表达式会产生明确定义的值。

    【讨论】:

      猜你喜欢
      • 2020-08-27
      • 1970-01-01
      • 2015-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多