三元运算符的结果不是右值

时间:2012-08-26 18:23:54

标签: c++ c++11 rvalue ternary

如果使用C ++ 11编译器编译该程序,则该向量不会移出该函数。

#include <vector>
using namespace std;
vector<int> create(bool cond) {
    vector<int> a(1);
    vector<int> b(2);

    return cond ? a : b;
}
int main() {
    vector<int> v = create(true);
    return 0;
}

如果您返回这样的实例,则会移动它。

if(cond) return a;
else return b;

这是demo on ideone

我用gcc 4.7.0和MSVC10试了一下。两者的行为方式相同。
我猜这种情况发生的原因是这样的:
三元运算符类型是左值,因为它在执行return语句之前被计算。此时a和b还不是xvalues(即将过期) 这个解释是否正确?

这是标准中的缺陷吗?
在我看来,这显然不是预期的行为和非常常见的情况。

2 个答案:

答案 0 :(得分:8)

以下是相关的标准报价:

12.8第32段:

  在以下情况下允许

复制省略 [...]

     
      
  • 在具有类返回类型的函数中的return语句中,当表达式是具有相同cv-unqualified的非易失性自动对象(除函数或catch子句参数之外)的名称时键入函数返回类型,通过将自动对象直接构造为函数的返回值,可以省略复制/移动操作
  •   
  • [当throw时,有条件]
  •   
  • [当来源是临时的,有条件的时候]
  •   
  • [当catch按值,有条件时]
  •   

第33段:

  

当满足或将满足复制操作的省略标准时,除了源对象是函数参数,并且要复制的对象由左值指定,重载决策以选择构造函数首先执行复制,就好像对象是由右值指定的一样。如果重载决策失败,或者所选构造函数的第一个参数的类型不是对象类型的rvalue引用(可能是cv-qualified),则再次执行重载决策,将对象视为左值。 [注意:无论是否发生复制省略,都必须执行此两阶段重载决策。如果未执行elision,它将确定要调用的构造函数,并且即使调用被省略,也必须可以访问所选的构造函数。 - 结束记录]

由于return (cond ? a : b);中的表达式不是简单的变量名称,因此不适用于复制省略或右值处理。也许有点不幸,但很容易想象一下这个例子会稍微进一步扩展,直到你为编译器实现带来期待。

当你知道它是安全的时,你可以通过明确地告诉std::move返回值来解决所有这些问题。

答案 1 :(得分:7)

这将解决它

return cond ? std::move(a) : std::move(b);

将三元运算符视为函数,就像代码

一样
return ternary(cond, a, b);

参数不会被隐式移动,你需要明确它。