将 std::unique_ptr 推回 std::vector 时编译器不会失败

Compiler doesn't fail when pushing back a std::unique_ptr into a std::vector

无法将 unique_ptr 推回 std::vector,因为它是不可复制的,除非使用 std::move。但是,设F是returns一个unique_ptr的函数,那么std::vector::push_back(F())操作是允许的。下面是一个例子:

#include <iostream>
#include <vector>
#include <memory>

class A {
  public:
    int f() { return _f + 10; }

  private:
    int _f = 20;
};

std::unique_ptr<A> create() { return std::unique_ptr<A>(new A); }


int main() {
  std::unique_ptr<A> p1(new A());

  std::vector< std::unique_ptr<A> > v;

  v.push_back(p1); // (1) This fails, should use std::move

  v.push_back(create()); // (2) This doesn't fail, should use std::move?

  return 0;
}

(2) 是允许的,但 (1) 是不允许的。这是因为返回值以某种方式隐式移动了吗?

(2)中,真的有必要用std::move吗?

std::move(X) 本质上意味着 "here, treat X as if it was a temporary object".

create() returns 一个临时的 std::unique_ptr<A> 开始,所以 move 是不必要的。


如果您想了解更多信息,请查看 value categories。您的编译器使用值类别来确定表达式是否引用临时对象 ("rvalue") 或不引用 ("lvalue")。

p1 是左值,create() 是右值。

在 C++11 中,我们获得了移动构造函数和右值语义。

std::move(X) 只是对右值的转换,它将 X 转换为 X&& 就是这样。比 move ctor 接管工作并移动构造函数通常 "steal" 参数持有的资源。 unique_ptr 有一个 move ctor。

函数 return 值已经是一个右值(除非函数 return 是一个左值引用,如注释中@HolyBlackCat 所指示)这将触发移动构造函数而无需任何额外的强制转换。由于 move ctor 是为 unique_ptr 定义的,因此它将编译。

另外 v.push_back(p1); 失败的原因是:您尝试使用左值调用复制构造函数但它失败了,因为 unique_ptr 没有复制构造函数。

std::vector::push_back() 有一个将右值引用作为输入的重载:

void push_back( T&& value );

create()的return值是一个未命名的临时值,即右值,因此可以将as-is传递给push_back()而不需要使用[=14] =] 就可以了。

std::move() 仅在传递命名变量时才需要,即左值,其中需要右值。

还值得一提的是,由于编译器能够移动未明确移动的对象 (NRVO),它也可以工作

#include <iostream>
#include <vector>
#include <memory>

class A {
  public:
    int f() { return _f + 10; }

  private:
    int _f = 20;
};

std::unique_ptr<A> create() {
    std::unique_ptr<A> x (new A);
    return x; 

}


int main() {
  std::unique_ptr<A> p1(new A());

  std::vector< std::unique_ptr<A> > v;

  //v.push_back(p1); // (1) This fails, should use std::move

  v.push_back(create()); // (2) This doesn't fail, should use std::move?

  return 0;
}