You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++中插入对象到unordered_map与vector时能否避免拷贝构造调用?

问题根源

你遇到的两次拷贝构造分别来自两个操作:

  1. map[x1] 访问时,左值x1会被拷贝一份作为unordered_map的key存入节点
  2. emplace_back(x2) 传入左值x2,会拷贝一份构造vector的新元素

最低改动方案:用移动替代拷贝

对于支持移动构造的类型(你给出的CustomClass已经实现了移动构造),仅需修改一行代码,即可用开销极低的移动构造完全替代两次拷贝构造:

// 将x1移动到map作为key,将x2移动到vector作为元素
map[std::move(x1)].emplace_back(std::move(x2));

修改后原来的两次拷贝构造会变成两次移动构造,对于仅包含基础成员的类型,移动构造的开销几乎可以忽略。


完全原地构造方案(零拷贝零移动,对象仅构造一次)

如果要完全避免拷贝、移动,实现对象仅在最终存储位置构造一次,可以配合C++17引入的try_emplace方法实现:

方案前提(二选一即可)

  1. 给目标类型添加接收std::istream&的构造函数,直接从输入流初始化对象:
CustomClass(std::istream& in) {
    std::cout << "istream constructor" << std::endl;
    in >> m_data;
}
  1. 若不能修改目标类型定义,可先将输入内容读取为基础类型的临时变量,再传入构造。

代码实现

适配istream构造的版本:
std::cout << "----" << std::endl;
// try_emplace直接在map节点原地构造key,返回迭代器
auto [it, _] = map.try_emplace(std::cin);
// emplace_back直接在vector的内存空间原地构造元素
it->second.emplace_back(std::cin);
std::cout << "----" << std::endl;
std::cout << it->second[0] << std::endl;
std::cout << "----" << std::endl;

整个过程仅触发两次自定义构造函数,无任何拷贝、移动操作。

不修改自定义类的通用版本:
std::cout << "----" << std::endl;
int key_val, elem_val;
// 先读取基础类型值,无自定义类开销
std::cin >> key_val >> elem_val;
// 直接用读取到的值原地构造map的key和vector的元素
auto [it, _] = map.try_emplace(key_val);
it->second.emplace_back(elem_val);
std::cout << "----" << std::endl;
std::cout << it->second[0] << std::endl;
std::cout << "----" << std::endl;

该方案对所有可从对应基础类型构造的类、基础类型都适用,全程无自定义类的拷贝、移动操作。


方案说明

  • unordered_map::try_emplace 是C++17新增的接口,会在键不存在时直接将传入的参数转发给键的构造函数,在map分配的节点内存上原地构造,完全避免了临时对象的拷贝或移动。
  • vector::emplace_back 会直接将传入的参数转发给元素的构造函数,在vector预分配的内存上原地构造元素,避免了临时对象的拷贝或移动。

内容的提问来源于stack exchange,提问作者TheMemeMachine

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 07:06:03