以下最小工作示例在使用选项1或选项2下的代码时编译,但在使用选项3下的代码时不编译。我假设emplace_back()
隐式使用/调用move
构造函数,那么为什么需要显式move()
?它与r-value
与l-value
有关吗?或者这与需要转让所有权的std::unique_ptr
有什么关系? (我仍然对这些概念不熟悉,特别是在这种情况下。)
为完整起见,除非push_back()
被调用,否则move()
的选项4也无法编译。
#include <iostream>
#include <vector>
#include <memory>
class Beta {
public:
Beta(int x, int y, int z): mX(x), mY(y), mZ(z) { };
int mX; int mY; int mZ;
};
class Alpha {
public:
std::vector<std::unique_ptr<Beta>> betaVec;
void addBeta(int x, int y, int z) {
// only choose one of the following options:
// option 1 (compiles)
std::unique_ptr<Beta> pBeta = std::make_unique<Beta>(x, y, z);
betaVec.emplace_back(move(pBeta));
// option 2 (compiles)
betaVec.emplace_back(std::make_unique<Beta>(x, y, z));
// option 3 (does not compile)
std::unique_ptr<Beta> pBeta = std::make_unique<Beta>(x, y, z);
betaVec.emplace_back(pBeta);
// option 4 (does not compile)
std::unique_ptr<Beta> pBeta = std::make_unique<Beta>(x, y, z);
betaVec.push_back(pBeta);
// option 5 (compiles)
std::unique_ptr<Beta> pBeta = std::make_unique<Beta>(x, y, z);
betaVec.push_back(move(pBeta));
}
};
int main() {
return 0;
}
注意:我不认为这是this question关于将unique_ptr
参数传递给函数的重复,即使链接问题的答案很有用,这是要求在函数中定义unique_ptr
,然后将其移动到成员vector
,以便它不会在函数末尾被销毁,而且,在这种情况下具体询问emplace_back()
。
此外,我认为在这种情况下给出解释是有用的,因为有时很难将解释从一个上下文转换到另一个上下文。谢谢!
答案 0 :(得分:3)
我假设
emplace_back()
隐式使用/调用移动构造函数
对不起,但你的假设是错的。 emplace_back
就地构造了向量中的对象,即不是从对象中复制/移动对象,而是直接构造元素,避免了复制/移动构造函数。
现在,如果你用相同的(但是另一个)对象构造对象,那么当然要使用copy或move构造函数,这就是你的情况。
那么为什么要明确
move()
因为您无法复制std::unique_ptr
。基本上,emplace_back
做了类似于此的事情:
new (place) T(std::forward<Ts>(args)...);
就像你做的那样:T a(std::forward<Ts>(args)...)
(仅限于施工,它实际上并没有做同样的事情。)
现在可能会更明显一点:
T option1(std::move(pBeta)); // ok, move
T option3(pBeta); // error, copy
是否与
r-value
与l-value
有关?或者这与需要转让所有权的std::unique_ptr
有关吗?
嗯,在某种程度上,是的。 std::unique_ptr
要求明确转让所有权,这就是为什么副本被禁用而移动不是(你仍然希望转移所有权!并且副本可以在任何地方发生 - 为什么std::auto_ptr
被弃用,然后被移除)。右值默认使用移动语义,而左值则不使用移动语义。通过使用std::move
,您正在进行从左值到初值的转换,有效地“隐藏”您拥有左值的事实,编译器将很乐意从它移动。