为什么 std::vector::emplace 调用析构函数而不调用任何复制构造函数?

Why does std::vector::emplace call destructor without any copy constructor called?

我将对象存储在 std::vector 中,我想尽可能避免调用析构函数。
我用移动替换了复制构造函数和赋值:

class Object
{
    Object(const Object&) = delete;
    Object(Object&&);
    Object& operator=(const Object&) = delete;
    Object& operator=(Object&&);
    [...]
};

我是这样初始化的:

std::vector<Object>   container;

container.reserve(42) // Reserve a lot in order to be sure it won't be a problem

然后,我使用 emplace_back 添加两个元素(构造函数采用一个 int 参数):

container.emplace_back(1);
container.emplace_back(3);

在那之前,一切都很好。但是后来我想在最后一个 emplace:

之前插入一个元素
auto it = container.end();

it--; // Last position.
it--; // Before last position.
container.emplace(it, 2);

但是这里调用了析构函数

我试图用 Valgrind 找出原因,emplace 函数调用 _M_insert_aux 调用了我的析构函数。

我怎样才能避免这种情况?

这是你无法避免的。这就是 vector 的工作原理。这是一个连续的数组。将新元素插入连续数组的唯一方法是将旧元素向下移动。这意味着使用移动分配将它们移动到新位置。

因此,如果您有以下矢量及其内容:

[5][12][16]

如果你在第二个元素之后插入,那么在某些时候你会得到这个:

[5][12][*][16]

其中“*”是移出元素的值。

然后是emplaceemplace 显式地构造值;这就是它的用途。但是,第三个元素中已经有一个活动对象:移出值。

因此,必须销毁此对象,然后才能在其位置构造新对象。因此必须调用析构函数。

如果您使用 insert 而不是 emplace,那么您仍然会调用析构函数。但这将是您传递给 insert 函数的对象的析构函数。

所以会有一个 "extra" 析构函数 在某处

但实际上,您不应该担心析构函数调用的次数。关注绝对成本。一般来说,如果你有一个只能移动的类型,移动值的析构函数会很便宜。