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

Result of ternary operator not an rvalue

本文关键字:结果 运算符 三元      更新时间:2023-10-16

如果使用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;

这是ideone上的演示。

我在gcc 4.7.0和MSVC10上尝试过。两者行为相同
我猜发生这种情况的原因是:
三元运算符类型是一个左值,因为它是在执行返回语句之前求值的。此时,a和b还不是x值(即将过期)
这个解释正确吗?

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

以下是相关的标准报价:

12.8第32段:

在以下情况下允许复制省略[…]

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

第33段:

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

由于return (cond ? a : b);中的表达式不是一个简单的变量名,因此它不符合复制省略或右值处理的条件。也许有点不幸,但很容易想象一次将示例进一步扩展一点,直到您对编译器实现产生了令人头疼的期望。

当然,当您知道返回值是安全的时,您可以通过明确地对std::move说返回值来绕过这一切。

这将修复

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

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

return ternary(cond, a, b);

参数不会被隐式移动,您需要使其显式。