【问题标题】:Using the result of compound assignment as an lvalue [duplicate]使用复合赋值的结果作为左值
【发布时间】:2013-05-07 09:12:03
【问题描述】:

我很惊讶这行得通:

double x = 3;
double y = 2;

(x *= 2) += y;
std::cout << x << std::endl;

结果是 8,这就是程序员试图达到的结果。但我认为赋值运算符返回了一个右值——你怎么能把它赋值给一个的结果?

【问题讨论】:

标签: c++ c rvalue compound-assignment


【解决方案1】:

内置类型的赋值运算符返回一个左值 在 C++ 中(与 C 不同)。但是你不能用它来修改 没有中间序列点的对象,所以你的例子是 未定义的行为(在 C++03 中—C++11 在这里发生了很大变化,并且 我似乎记得其中一个结果使您的代码 定义)。

不管未定义行为的情况如何, 你最好写:

x = 2 * x + y;

它更具可读性。赋值运算符的事实 左值的结果实际上仅在结果为 立即绑定到引用:

T&
SomeClass::f()
{
    //  ...
    return aTinSomeClass += 42;
}

即使那样,我也会用两条语句来写。

(C++ 中的一般规则是,如果运算符的结果 对应内存中一个对象的值,那么它就是一个 左值。 C 中没有一般规则。)

【讨论】:

  • 我当然同意您的版本更具可读性 - 我正在查看其他人的(旧)代码,这只是它引发的......好奇心之一。你能稍微扩展一下序列点吗?也许参考标准“你不能在没有中间序列点的情况下使用它来修改对象”?
  • 在 C++11 之前的引用是第 5 节的第 4 段:“在前一个和下一个序列点之间,一个标量对象的存储值最多只能通过表达式的评估修改一次。 "序列点是完整表达式的结尾,调用或从函数返回,以及极少数运算符:&amp;&amp;||?: 和逗号运算符(但不是用于分隔函数参数的逗号) .
  • 谢谢。好奇g++ -Wsequence-point 没有抱怨这个(g++ 4.6.3)。
  • @Tom:在C++11中,行为被定义为:5.17/1说“赋值顺序在左右操作数的值计算之后,赋值的值计算之前表达”。但我同意你应该坚持使用可读版本。
  • @MikeSeymour 是的。我对 C++11 对其进行了定义有某种模糊的记忆。我关于未定义行为的 cmets 基于 C++03;我真的没有时间研究新规范的所有含义(扩展到涵盖多线程访问)。
【解决方案2】:

在 C++ 中,赋值运算符的结果,包括复合赋值运算符(例如 *=)是左值,因此是可赋值的。

在 C 中它们是 r 值,因此您的代码是无效的 C 代码。

【讨论】:

    【解决方案3】:

    在 C++ 中,复合赋值运算符返回左值,根据 §5.17/1:

    赋值运算符 (=) 和复合赋值运算符都从右到左分组。都需要一个可修改的左值作为左操作数,并返回一个指向左操作数的左值。

    在这种情况下,*= 的使用会返回一个左值,表示对象 x,然后将其用作+= 运算符的左操作数。

    也许您正在考虑像 +* 这样的简单算术运算符,它们确实返回右值。

    在 C 中,这些运算符不返回左值,因此无法进一步分配结果。

    【讨论】:

    • 或许他在想C。
    • @JamesKanze Aw,我讨厌在我写完答案后发现问题也被标记为 C。
    • 我认为这不是问题所在。标记与否,他的代码使用std::cout,所以我们真的只关心C++。但如果他确实先学习了 C,他可能只是假设规则与 C 中的相同。
    猜你喜欢
    • 2018-02-04
    • 1970-01-01
    • 2011-09-03
    • 2011-03-12
    • 2018-10-19
    相关资源
    最近更新 更多