将 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;
}
无法将 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;
}