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

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

本文关键字:std vector 失败 推回 unique 编译器 ptr      更新时间:2023-10-16

除非使用std::move,否则unique_ptr不能推回std::vector,因为它是不可复制的。但是,让我们F是一个返回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)本质上的意思是"在这里,将X视为临时对象"。

create()首先返回一个临时std::unique_ptr<A>,因此不需要move


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

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

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

void push_back( T&& value );

create()的返回值是一个未命名的临时值,即右值,因此可以按原样传递给push_back(),而无需对其使用std::move()

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

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

std::move(X) 只是一个转换到 rvalue 的转换,它将 X 转换为 X&仅此而已。比移动 ctor 接管工作,移动构造函数通常会"窃取"参数持有的资源。unique_ptr有一个移动控制者。

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

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

还值得知道的是,由于编译器能够移动未显式移动的对象(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;
}