为什么C++ std :: map :: operator []使用inplace new?

srm*_*srm 5 c++ stl new-operator

如果您使用带有值类型的C++ std :: map(和其他容器),您会注意到插入到地图中会调用元素类型的析构函数.这是因为C++规范要求operator []的实现等效于:

(*((std::map<>::insert(std::make_pair(x, T()))).first)).second
Run Code Online (Sandbox Code Playgroud)

它调用您的类型的默认构造函数以构建该对.然后将该临时值复制到地图中,然后进行破坏.可以在此stackoverflow帖子codeguru中找到对此的确认.

我发现奇怪的是,这可以在不需要临时变量的情况下实现,但仍然是等效的.C++的一个特性叫做"inplace new".std :: map和其他容器可以为对象分配空的空间,然后在分配的空间上显式调用元素的默认构造函数.

我的问题:为什么我所见过的std :: map的实现都没有使用new来优化这个操作?在我看来,它将大大提高这种低级别操作的性能.但很多人都研究过STL代码库,所以我认为必须有这样的原因.

Yak*_*ont 4

一般来说,像较低级别的操作一样指定较高级别的操作[]是一个好主意。

在 C++11 之前,[]如果不使用insert.

在 C++11 中,添加了std::map<?>::emplacefor 和类似的东西std::pair使我们能够避免这个问题。如果您重新定义它并使用这种就地构造,则额外的(希望被省略的)对象创建将会消失。

我想不出有什么理由不这样做。我鼓励您提出标准化建议。

为了演示对 a 的无复制插入std::map,我们可以执行以下操作:

#include <map>
#include <iostream>

struct no_copy_type {
  no_copy_type(no_copy_type const&)=delete;
  no_copy_type(double) {}
  ~no_copy_type() { std::cout << "destroyed\n"; }
};
int main() {
  std::map< int, no_copy_type > m;
  m.emplace(
    std::piecewise_construct,
    std::forward_as_tuple(1),
    std::forward_as_tuple(3.14)
  );
  std::cout << "destroy happens next:\n";
}
Run Code Online (Sandbox Code Playgroud)

现场示例——如您所见,没有生成临时文件。

所以如果我们替换

(*((std::map<>::insert(std::make_pair(x, T()))).first)).second
Run Code Online (Sandbox Code Playgroud)

(*
  (
    (
      std::map<>::emplace(
        std::piecewise_construct,
        std::forward_as_tuple(std::forward<X>(x)),
        std::forward_as_tuple()
    )
  ).first
).second
Run Code Online (Sandbox Code Playgroud)

不会创建临时文件(添加空格以便我可以跟踪()s)。