22

可能重复:
C++11 右值和移动语义混淆

我认为正确的是

std::string GetLine()
{
std::string str;
std::getline(std::cin, str);
return std::move(str);
}

但是在这个链接http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html (检查标题部分 Returning an explicit rvalue-reference from a功能)

这是#1 google search hit for move semantics 显示了类似的函数签名

int&& GetInt()
{
int x = 0;
// code here
return std::move(x);
}

从我在其他地方读到的 && 表示右值引用,因此在这种情况下,它返回对不存在的对象的引用。

那么它是哪一个?

(是的,我知道移动 int 没有真正的好处,但问题是是否在第一个函数中使用 std::string 或 std::string&& 的返回类型。如果这对所有类型都应该这样做。)

4

3 回答 3

41

您绝对正确,该int&& GetInt()示例是错误的,并且正在返回对已销毁对象的引用。但是,除非我错过了,否则您发布的链接实际上并未显示任何返回对局部变量的引用的代码。相反,我看到一个对全局变量的引用被返回,这没关系。

以下是返回时如何使用移动语义:

std::string func()
{
    std::string rv;
    /* ... */
    return rv;
}

std::move()返回对象时通常不应该使用。这样做的原因是,在 RVO 可能发生的任何时候都已经隐式允许移动,并且使用std::move()将抑制 RVO。所以使用std::move()永远不会比正常返回更好,而且通常会更糟。


同样,使用std::move()可能比简单地命名要返回的变量更糟糕,因为它抑制了返回值优化。返回值优化允许将对象返回给调用者,而无需复制该对象

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

— [class.copy] 12.8/31

但是 usingstd::move()可以防止返回表达式成为您要返回的对象的名称。相反,表达式更复杂,语言不再允许对其进行特殊处理。

仅仅命名对象并不比使用更糟糕的原因std::move()是因为有另一条规则说表达式已经可以被视为右值而不需要std::move().

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

于 2012-08-17T18:51:32.943 回答
11

回答这个问题,有点:返回一个string. 什么都不做move,而是使用(依赖)RVO:

std::string func()
{
    std::string rv;
    /* ... */
    return rv;
}

通常应该这样做。您不能返回对临时的(r 值与否)引用。

于 2012-08-17T18:43:43.457 回答
4

无需说return std::move(str);是否str是局部变量:如果变量满足返回值优化的标准,则在return语句中该变量将绑定到右值引用。

另外,请注意,您可能不应该返回对局部变量的引用,无论是左值还是右值引用。

总而言之,你应该有:

int foo() { int x; /*...*/ return x; }

std::string bar() { std::string str; /*...*/ return str; }
于 2012-08-17T18:53:27.683 回答