提问人:Venci 提问时间:10/17/2021 最后编辑:Venci 更新时间:10/17/2021 访问量:422
使用 copy-construcor 将对象推入向量时无法避免复制
Unable to avoid copying while pushing objects with copy-construcor into a vector
问:
我试图避免复制 和 .但是当我尝试这样做时,我发现自己得到了 3 份副本,原因我真的无法理解。 实际上有助于避免复制,但实际上对它没有任何作用(与本例的工作方式相同)。代码如下:emplace_back()
reserve()
reserve()
emplace_back()
push_back()
struct Vertex
{
size_t x, y, z;
Vertex(size_t x, size_t y, size_t z)
{
this->x = x;
this->y = y;
this->z = z;
}
Vertex(const Vertex& v)
: x(v.x), y(v.y), z(v.z)
{
std::cout << "\nCOPIED!\n";
}
};
int main()
{
std::vector<Vertex> vert;
vert.reserve(3);
vert.emplace_back(Vertex(1, 2, 3));
vert.emplace_back(Vertex(4, 5, 6));
vert.emplace_back(Vertex(7, 8, 9));
return 0;
}
输出是“复制!”的 3 倍。好吧,我尝试过这样的事情:
vert.emplace_back(std::move(Vertex(1, 2, 3)));
vert.emplace_back(std::move(Vertex(4, 5, 6)));
vert.emplace_back(std::move(Vertex(7, 8, 9)));
将我的对象转换为 r 值,但我再次得到了 3 次“复制!
然后我尝试在有和没有的情况下推送同一个对象 3 次,再次得到相同的结果:std::move
Vertex vertex(1, 2, 3);
vert.emplace_back(vertex);
vert.emplace_back(vertex);
vert.emplace_back(vertex);
或
Vertex vertex(1, 2, 3);
vert.emplace_back(std::move(vertex));
vert.emplace_back(std::move(vertex));
vert.emplace_back(std::move(vertex));
我不明白我做错了什么。我使用 MSVS 2022 预览版 C++14。我也尝试在 C++14/17/20 上执行此操作,结果相同。有没有办法摆脱所有的副本?或者也许我对情况的理解是错误的?
答:
std::move
在这里没有用。临时对象已经是一个 prvalue,因此将其转换为 xvalue 不会更改任何内容。该类没有移动构造函数,因此复制初始化无法移动;它必须复制。隐式移动构造函数已被用户定义的复制构造函数禁止。虽然,无论如何,移动构造函数都不能比这个类的复制构造函数快。Vertex
工作方式是将参数转发给元素的构造函数。如果传递的参数是元素类型的对象,则调用接受该类的另一个实例的构造函数,即复制构造函数(或具有该实例的类的移动构造函数)。emplace_back
与其创建一个临时对象,并将其作为参数传递给 ,不如将三个可以转发给构造函数的对象。这样,您可以完全避免复制(和移动)对象:Vertex
emplace_back
size_t
Vertex(size_t, size_t, size_t)
Vertex
vert.emplace_back(1, 2, 3);
vert.emplace_back(4, 5, 6);
vert.emplace_back(7, 8, 9);
由于您提供了 的自定义复制构造函数,因此不会自动生成移动构造函数。如您所见,尝试移动此结构将使用 copy 构造函数。struct Vertex
添加自定义移动构造函数:
Vertex(Vertex &&v)
: x(std::move(v.x)), y(std::move(v.y)), z(std::move(v.z))
{
std::cout << "MOVED!\n";
}
但请注意,除了打印之外,这里既不需要自定义复制,也不需要移动构造函数。隐式生成的就足够了。
std::move(Vertex(1, 2, 3))
std::move
这里不会改变任何内容,因为已经是右值了。Vertex(1, 2, 3)
但。。。。工作方式与本例相同
emplace_back()
push_back()
是的。为了让它发挥作用,你需要将构造函数参数直接传递给它,正如 @eerorika 的回答中提到的:
vert.emplace_back(1, 2, 3);
这将通过直接在向量中构造对象来摆脱复制/移动。
通过添加默认构造函数使其成为 POD(普通旧数据):
struct Vertex
{
size_t x, y, z;
Vertex() = default;
Vertex(size_t xx, size_t yy, size_t zz) : x{xx}, y{yy}, z{zz} {}
};
评论
vert.emplace_back(1, 2, 3);
emplace_back
push_back
std::move
std::move
Vertex(Vertex&&) = default;