return 语句何时需要显式移动?

在对另一个问题的评论中,Jonathan Wakely 回应了我的声明:

In a comment to another question Jonathan Wakely responds to my statement:

对于局部变量函数返回,您永远不需要显式移动价值.这是隐含的移动

You never need explicit move for a local variable function return value. It's implicit move there

->

...永远不要说永远...如果局部变量,您需要显式移动与返回类型不同,例如std::unique_ptrf() { auto p = std::make_unique<派生>();p->foo();返回 p;},但是如果类型相同,它会尽可能移动......

... never say never ... You need an explicit move if the local variable is not the same type as the return type, e.g. std::unique_ptr<base> f() { auto p = std::make_unique<derived>(); p->foo(); return p; }, but if the types are the same it will move if possible ...

所以有时我们可能不得不在返回时移动一个局部变量.

So it seems sometimes we may have to move a local variable on return.

例子

std::unique_ptr<base> f() { 
  auto p = std::make_unique<derived>();
  p->foo(); 
  return p; 
}

很好,因为它给出了 编译错误

> prog.cpp:10:14: error: cannot convert ‘p’ from type
> ‘std::unique_ptr<derived>’ to type ‘std::unique_ptr<derived>&&’

但我想知道一般情况下是否有很好的机会检测到这一点 - 这是语言规则或 unique_ptr 的限制吗?

but I'm wondering whether there is a good chance to detect this in general -- and is this here a limit of the language rules or of unique_ptr??

推荐答案

更新:

现代编译器版本不需要显式移动.

Explicit move should not be needed in modern compiler versions.

核心 DR 1579 更改了规则这样即使类型不同,返回值也会被视为右值.GCC 5 为 C++11 和 C++14 实现了新规则.

Core DR 1579 changed the rules so that the return value will be treated as an rvalue even when the types are not the same. GCC 5 implements the new rule, for C++11 as well as C++14.

原答案:

这不是 unique_ptr 的限制,而是语言的限制,同样的限制适用于任何调用带有右值引用的转换构造函数的 return 语句:

This is not a limitation of unique_ptr, it's a limitation of the language, the same limitation applies to any return statement that calls a converting constructor taking an rvalue reference:

struct U { };

struct T {
  T(U&&) { }
};

T f() {
  U u;
  return u;  // error, cannot bind lvalue to U&&
}

这不会编译,因为 [class.copy]/32 说:

This will not compile because [class.copy]/32 says:

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

When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.

这意味着 return 语句中的表达式只有在符合复制/移动省略(又名 NRVO)条件时才能被视为右值,但这限制性太强,因为这意味着它只适用当类型完全相同时,即使变量总是超出范围,所以总是将 is 视为右值是合理的(从技术上讲,它是一个 xvalue,一个 expiring 值.)

This means that the expression in a return statement can only be treated as an rvalue if it is eligible for copy/move elision (aka NRVO) but that is too restrictive because it means it only applies when the type is exactly the same, even though the variable is always going out of scope so it would be reasonable to always treat is as an rvalue (technically as an xvalue, an expiring value.)

这是最近建议 由 Richard Smith(之前由 Xeo 编写),我认为这是一个非常好的主意.

This was recently suggested by Richard Smith (and previously by Xeo) and I think it's a very good idea.

相关文章