为了账号安全,请及时绑定邮箱和手机立即绑定

从函数返回unique_ptr

从函数返回unique_ptr

C++
慕桂英4014372 2019-07-23 16:38:46
从函数返回unique_ptrunique_ptr<T>不允许复制构造,而是支持移动语义。然而,我可以unique_ptr<T>从函数返回一个并将返回的值赋给变量。#include <iostream>#include <memory>using namespace std;unique_ptr<int> foo(){   unique_ptr<int> p( new int(10) );   return p;                   // 1   //return move( p );         // 2}int main(){   unique_ptr<int> p = foo();   cout << *p << endl;   return 0;}上面的代码按预期编译和工作。那么该行如何1不调用复制构造函数并导致编译器错误呢?如果我必须使用line 2而不是它有意义(使用line 2也可以,但我们不需要这样做)。我知道C ++ 0x允许此异常,unique_ptr因为返回值是一个临时对象,一旦函数退出就会被销毁,从而保证返回指针的唯一性。我很好奇这是如何实现的,它是在编译器中特殊的,还是在语言规范中有一些其他条款可以利用?
查看完整描述

3 回答

?
肥皂起泡泡

TA贡献1829条经验 获得超6个赞

这个漏洞是否在语言规范中有其他一些条款?

是的,见12.8§34和§35:

当满足某些条件时,允许实现省略类对象的复制/移动构造[...]复制/移动操作的省略,称为复制省略,允许在...中的返回语句中。具有类返回类型的函数,当表达式是非易失性自动对象的名称时,具有与函数返回类型相同的cv-nonqualified类型[...]

当满足复制操作的省略标准并且要通过左值指定要复制的对象时,首先执行用于选择复制的构造函数的重载决策,就好像该对象由右值指定一样


只是想再增加一点,按值返回应该是默认选择,因为在最坏的情况下,返回语句中的命名值,即C ++ 11,C ++ 14和C ++ 17中没有elisions的处理作为一个左值。因此,例如,以下函数使用-fno-elide-constructors标志进行编译

std::unique_ptr<int> get_unique() {
  auto ptr = std::unique_ptr<int>{new int{2}}; // <- 1
  return ptr; // <- 2, moved into the to be returned unique_ptr}...auto int_uptr = get_unique(); // <- 3

在编译时设置标志,在此函数中发生两个移动(1和2),然后稍后移动(3)。


查看完整回答
反对 回复 2019-07-23
?
狐的传说

TA贡献1804条经验 获得超3个赞

这绝不具体std::unique_ptr,但适用于任何可移动的类。由于您按价值返回,因此语言规则保证了这一点。编译器尝试删除副本,如果无法删除副本则调用移动构造函数,如果无法移动则调用复制构造函数,如果无法复制则无法编译。

如果你有一个接受std::unique_ptr作为参数的函数,你将无法将p传递给它。您必须显式调用move构造函数,但在这种情况下,您不应在调用后使用变量p bar()

void bar(std::unique_ptr<int> p){
    // ...}int main(){
    unique_ptr<int> p = foo();
    bar(p); // error, can't implicitly invoke move constructor on lvalue
    bar(std::move(p)); // OK but don't use p afterwards
    return 0;}


查看完整回答
反对 回复 2019-07-23
?
幕布斯7119047

TA贡献1794条经验 获得超8个赞

unique_ptr没有传统的复制构造函数。相反,它有一个使用右值引用的“移动构造函数”:

unique_ptr::unique_ptr(unique_ptr && src);

右值参考(双&符号)仅绑定到右值。这就是当您尝试将左值unique_ptr传递给函数时出现错误的原因。另一方面,从函数返回的值被视为右值,因此自动调用移动构造函数。

顺便说一句,这将正常工作:

bar(unique_ptr<int>(new int(44));

这里的临时unique_ptr是一个右值。


查看完整回答
反对 回复 2019-07-23
  • 3 回答
  • 0 关注
  • 1642 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信