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;
}

如果您像这样返回实例,它会被移动。

if(cond) return a;
else return b;

这是关于 ideone 的演示

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

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

4

2 回答 2

8

这将解决它

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

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

return ternary(cond, a, b);

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

于 2012-08-26T18:27:32.807 回答
8

以下是相关的标准报价:

12.8 第 32 段:

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

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

第33段:

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

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

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

于 2012-08-26T18:56:52.773 回答