什么时候返回语句需要显式移动?

Mar*_* Ba 18 c++ return implicit-conversion move-semantics c++11

对另一个问题评论中, Jonathan Wakely回应了我的陈述:

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

- >

...永远不要说永远......如果局部变量与返回类型的类型不同,则需要显式移动,例如std::unique_ptr<base> f() { auto p = std::make_unique<derived>(); p->foo(); return p; },但如果类型相同,则可能会移动...

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

这个例子

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

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

> prog.cpp:10:14: error: cannot convert ‘p’ from type
> ‘std::unique_ptr<derived>’ to type ‘std::unique_ptr<derived>&&’
Run Code Online (Sandbox Code Playgroud)

但我想知道是否有一个很好的机会来检测这一般 - 这是这里的语言规则或unique_ptr ??

Jon*_*ely 21

更新:

在现代编译器版本中不应该需要显式移动.

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

原始答案:

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

struct U { };

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

T f() {
  U u;
  return u;  // error, cannot bind lvalue to U&&
}
Run Code Online (Sandbox Code Playgroud)

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

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

这意味着return语句中的表达式只有在符合复制/移动省略(即NRVO)的情况下才能被视为右值,但这种限制性太强,因为这意味着它只适用于类型完全相同的情况,即使变量总是超出范围,所以总是把它作为一个rvalue(在技术上作为xvalue,一个到期值)是合理的.

这是最近提出理查德·史密斯(以前由XEO),我认为这是一个很好的主意.

  • 我很久以前就把这个建议发给了迈克和理查德,以便在布里斯托尔考虑,但似乎他们没有达到目的.:( (3认同)
  • 这应该更新,以反映http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1579的决议. (3认同)
  • 哦,[这里](http://open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#1579)是相关的核心/进化问题.再次浏览我的邮件,杰弗里·亚斯金似乎真的打败了我.我添加了建议,也是从局部变量的子对象移动.:) (2认同)
  • @Xeo单个对象的合理状态在其包含的较大对象的上下文中不一定是合理的状态.对于公众成员来说当然*不应该*(因为任何人都可以设置它们),但是有100%的确定性,有许多人滥用公共成员并且(有希望记录)他们可能包含或不包含的内容的要求.因此,这样的改变可能会破坏遗留代码. (2认同)
  • 该成员可能不是公共的,您可能从可以访问该对象的朋友或成员函数返回它.我同意@Joe,移动一个对象的_part_可以打破它的不变量,所以你要么需要一种方法来显式地禁用隐式移动,要么保守而不是为子对象做 (2认同)