为什么可以在没有 std::move 的情况下返回 std::unique_ptr ?

2024-04-12

unique_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不调用复制构造函数并导致编译器错误?如果我必须使用线路2相反,它是有意义的(使用行2也可以,但我们不需要这样做)。

我知道 C++0x 允许此异常unique_ptr由于返回值是一个临时对象,一旦函数退出就会被销毁,从而保证了返回指针的唯一性。我很好奇这是如何实现的,编译器中是否有特殊情况,或者语言规范中是否有其他条款可以利用?


语言规范中是否还有其他条款可以利用?

是的,参见 12.8 §34 和 §35:

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

当满足复制操作省略的条件并且要复制的对象由左值指定时, 首先执行重载决策以选择副本的构造函数就好像该对象是由右值指定的.


只是想再补充一点,按值返回应该是这里的默认选择,因为在最坏的情况下,即在 C++11、C++14 和 C++17 中没有省略的情况下,会处理 return 语句中的命名值作为右值。例如,以下函数编译时使用-fno-elide-constructors flag

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)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么可以在没有 std::move 的情况下返回 std::unique_ptr ? 的相关文章

随机推荐