【问题标题】:Implicitly treating returned lvalue as rvalue隐式将返回的左值视为右值
【发布时间】:2012-07-15 14:56:09
【问题描述】:

12.8 复制和移动类对象 [class.copy] §31 和 §32 说:

在具有类返回类型的函数的返回语句中,当表达式是具有相同的非易失性自动对象(函数或 catch 子句参数除外)的名称时 cv-unqualified类型作为函数返回类型,直接在函数返回值中构造自动对象即可省略复制/移动操作

当满足或将满足省略复制操作的条件时,除了源对象是函数参数的事实,并且要复制的对象由左值指定时,重载决策选择构造函数首先执行复制,就好像对象是由右值指定的一样。

因此我们可以这样写:

unique_ptr<int> make_answer()
{
    unique_ptr<int> result(new int(42));
    return result;   // lvalue is implicitly treated as rvalue
}

但是,我注意到 g++ 4.6.3 也接受 不是名称 的左值,例如:

    return (result);
    return *&result;
    return true ? result : result;

相比之下,return rand() ? result : result; 不起作用。编译器的优化器是否干扰了语言语义?在我解释标准时,return (result); 不应该编译,因为(result) 不是名称,而是带括号的表达式。我是对还是错?

【问题讨论】:

  • 删除了ownership 标签,因为它只适用于特定示例,并且可以轻松更改为不涉及所有权的内容。,
  • 引用最好保持其上下文部分完整,从“这种复制/移动操作的省略,称为复制省略,在以下情况下是允许的(可以组合以消除多个副本): " 或从段落的前一点开始。
  • 我现在可以确认 VS2010 表现出相同的行为,在前两种情况下生成对移动构造函数的调用,但在第三种情况下抱怨无法访问复制 ctor。有趣的是,Intellisense 在第二种情况下已经抱怨应该调用复制 ctor 并且无法访问。 FWIW,GCC 4.7.1 传递所有 3。

标签: c++ c++11 return move-semantics


【解决方案1】:

关于括号表达式 [√]

您在谈论带括号的表达式时是错误的,并且当它被 返回 并且只包含 的名称时,它不应该能够触发 move可移动的对象。

5.1.1/1      一般      [expr.prim.general]

带括号的表达式是一个主表达式,其类型和 值与封闭表达式的值相同。这 括号的存在不会影响表达式是否为左值。 括号内的表达式可以用在与括起来的表达式完全相同的上下文中,并且具有相同的含义,除非另有说明。


关于 constexpr 条件运算符 [╳]

我对 constant-expressions 和他条件运算符 的标准的解释是return true ? result : result 的使用很好-行为,因为它是一个常量表达式,因此等价于 return result;

我现在已经更仔细地阅读了该标准,并且没有任何地方说 constant conditional-expression 与只有 "returned" 表达式相同已经写好了。

true ? <expr1> : <expr2>; // this is not the same as just writing <expr1>;

关于return *&result; [╳]

在 C99 中明确指出 *&amp;result精确 等同于编写 result 的,而在 C++ 规范中并非如此。

虽然我们都同意使用*&amp;result 确实会产生与result 相同的左值,但根据标准*&amp;result(当然)不是一个表达式“表达式是非易失性自动对象的名称”.

当然,表达式包含一个合适的名称,但不仅如此。


总结一下……

return result; // #1, OK

return (result);                  // as described earlier, OK
return true ? result : result;    // as described earlier, ill-formed
return rand () ? result : result; // as described earlier, ill-formed
return *&result;                  // as described earlier, ill-formed

【讨论】:

  • 第二种情况也应该是格式错误的,因为三元运算符是一个表达式,并不表示自动对象的名称。优化不允许改变程序的可观察行为(复制省略等除外)。
  • 我不太确定。三元运算符可用于例如constexpr 函数或数组索引。
  • @DeadMG 我是。作为常量表达式的条件不会将源代码从条件运算符表达式更改为名称。在常量表达式中使用它是无关紧要的。
  • 关于*&amp;,您可能正在考虑C,其中&amp;*§6.5.3.2/3 (C99) 明确定义为无操作
  • @Cubbi 啊,谢谢,我要去买烟,但我回来后会更新这个答案。谢谢!
【解决方案2】:

除非另有说明(例如,在 ADL 规则中所做的那样,或者在另一个示例中通过 decltype 来完成),否则带括号的表达式等同于它们的未带括号的表达式。有时,当某些东西以这种方式不等价时,有时会很棘手(例如,ADL 规则没有明确提到“无括号”,但它们使用了显式的非终结语法,并且示例清楚地表明括号不是认为是等价的)。

对于其他问题:是的,GCC 直接对 AST 进行了多项优化,使其接受各种无效程序,如下所示

int a = 42;
int *p = 0 * a;

【讨论】:

  • @ildjarn 屏住呼吸 :) 它也接受char *x = "hello folks" + 0;,尽管规范只允许对字符串文字进行这种特殊转换,而不是在任意指针表达式上计算为@987654323 类型的相同地址@.
猜你喜欢
  • 1970-01-01
  • 2014-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多