当 insert() 插入到 std::map 中时,为什么 copy-contructor 被调用两次?

When insert()ing into a std::map why is the copy-contructor called twice?

提问人:StoneThrow 提问时间:10/16/2022 更新时间:10/16/2022 访问量:83

问:

为什么在此代码中调用了两次复制构造函数?

// main.cpp
#include <iostream>
#include <map>
#include <string>

using namespace std;

class C {
private:
  int i_;
  char c_;

public:
  C(int i, char c) : i_(i), c_(c) { cout << "ctor" << endl; }
  C(const C& other) { cout << "copy-ctor" << endl; }
};

int main(int argc, char* argv[]) {
  map<string, C> m;

  m.insert({"hello", C(42, 'c')});

  return 0;
}

构建和输出:

$ g++ --version && g++ -g ./main.cpp && ./a.out
g++ (Debian 6.3.0-18+deb9u1) 6.3.0 20170516
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

ctor
copy-ctor
copy-ctor
C++ STL 插入 复制构造函数 stdmap

评论

0赞 SoronelHaetir 10/16/2022
请注意,如果使用最新版本的 c++ 标准库,则可以使用 map::emplace 并避免额外的副本。
3赞 JeJo 10/16/2022
地图插入过程分为三步:创建地图的值类型 -> 复制到 std::map::insert 函数本地 ->复制到树的正确位置的地图。这整个过程可以通过以下piecewise_construct来避免:在正确的位置构造条目。std::map::emplacem.emplace( std::piecewise_construct, std::forward_as_tuple("hello"), std::forward_as_tuple(42, 'c') );
3赞 PaulMcKenzie 10/16/2022
为了更好地了解正在发生的事情,您应该输出 的值,而不仅仅是一条简单的消息: -- -- 这样,您可以看到正在创建的对象。thiscout << "ctor: " << this << endl;cout << "copy-ctor " << this << endl;

答:

3赞 273K 10/16/2022 #1

映射值类型为 。 不可移动,因此不可移动。std::pair<const int, C>Cstd::pair<const int, C>

m.insert({"hello", C(42, 'c')});创建 并将其复制到 中的局部变量,然后将 复制到地图存储桶。Cpairvalueinsertpair

m.emplace("hello", C(42, 'c'));将仅复制一次到存储桶。C

Compiler options...
Program returned: 0
Program stdout
ctor
copy-ctor

评论

1赞 JeJo 10/16/2022
如果 emplace 使用正确,则根本不会有任何副本:gcc.godbolt.org/z/P6MEfjnsfm.emplace( std::piecewise_construct, std::forward_as_tuple("hello"), std::forward_as_tuple(42, 'c') );
0赞 273K 10/16/2022
@JeJo 是的,我看到了你对这个问题的精彩评论,但我没有时间向 OP 解释。