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

Pai*_*ait 11 c++ rvalue ternary c++11

如果使用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;
}
Run Code Online (Sandbox Code Playgroud)

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

if(cond) return a;
else return b;
Run Code Online (Sandbox Code Playgroud)

这是关于ideone演示.

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

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

Dan*_*ani 8

这将解决它

return cond ? std::move(a) : std::move(b);
Run Code Online (Sandbox Code Playgroud)

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

return ternary(cond, a, b);
Run Code Online (Sandbox Code Playgroud)

参数不会隐式移动,您需要将其显式化.


asc*_*ler 8

以下是相关的标准报价:

12.8第32段:

在下列情况下允许复制省略 [...]

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

第33段:

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

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

当然,std::move当您知道它是安全的时,您可以明确地告诉返回值,从而解决所有这些问题.