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

C++中unordered_map::insert的隐式转换与构造函数调用问题

C++ unordered_map insert 构造函数调用疑问解析

代码

#include <iostream>
#include <unordered_map>
#include <utility>

using namespace std;

struct Foo {
    Foo(const int value) : val(value) {
        cout << "Foo(int), val: " << val << '\n';
    }
    Foo(Foo & foo) {
        val = foo.val;
        cout << "Foo(Foo &)" << '\n';
    }
    Foo(const Foo & foo) {
        val = foo.val;
        cout << "Foo(const Foo &)" << '\n';
    }
    Foo(Foo && foo) {
        val = foo.val;
        cout << "Foo(Foo &&)" << '\n';
    }
    ~Foo() { cout << "~Foo(), val: " << val << '\n'; }
    Foo& operator=(const Foo& rhs)
    {
        cout << "Foo& operator=(const Foo& rhs), rhs.val: " << rhs.val;
        val = rhs.val;
        return *this;
    }
    bool operator==(const Foo& rhs) const { return val == rhs.val; }
    bool operator<(const Foo& rhs)  const { return val < rhs.val; }

    int val;
};
template<> struct std::hash<Foo> {
    size_t operator()(const Foo& f) const { return hash<int>{}(f.val); }
};

int main()
{
    std::unordered_map<Foo, int> mp;
    mp.insert(std::pair<Foo, int>{1, 50});
    std::cout << '\n';
    mp.insert(std::pair<const Foo, int>{2, 60});
    std::cout << '\n';

    std::cout << "exiting main()\n";
}

运行输出

Foo(int), val: 1
Foo(Foo &&)
~Foo(), val: 1

Foo(int), val: 2
Foo(const Foo &)
~Foo(), val: 2

exiting main()
~Foo(), val: 1
~Foo(), val: 2

疑问与解答

疑问1:为什么mp.insert(std::pair<Foo, int>{1, 50})可以编译?是否发生了隐式转换?

可以编译,确实发生了隐式转换,具体过程如下:

  1. std::pair<Foo, int>{1, 50}先调用Foo(int)构造临时Foo对象,输出Foo(int), val: 1,同时构造出临时的std::pair<Foo, int>对象。
  2. std::unordered_map<Foo, int>的value_type是std::pair<const Foo, int>,而std::pair提供了模板构造函数,允许从std::pair<U, V>隐式构造std::pair<T1, T2>,只要U可转换为T1、V可转换为T2。这里Foo可以直接转换为const Foo(仅添加const修饰,无需构造新对象),int与int完全匹配,因此临时的std::pair<Foo, int>可以隐式转换为std::pair<const Foo, int>右值,匹配insert的value_type&&重载。
  3. 由于传入的是右值,map内部分配节点时会通过std::move将pair中的Foo移动到节点中,因此调用Foo(Foo &&)移动构造函数,输出对应日志。
  4. 临时的std::pair<Foo, int>对象销毁,其内部的Foo被析构,输出~Foo(), val: 1。

转换过程中没有调用拷贝构造函数,是因为Foo到const Foo的转换只是添加const属性,没有创建新的Foo对象,仅改变了对象的类型修饰。

疑问2:为什么第一次insert调用Foo的移动构造函数,而第二次insert调用Foo的拷贝构造函数?

  • 第一次insert传入的是std::pair<Foo, int>临时对象(右值),转换为std::pair<const Foo, int>后仍是右值。此时pair中的Foo是可修改的右值,可以绑定到Foo&&参数,因此map内部移动构造节点中的Foo时,调用Foo(Foo &&)移动构造函数。
  • 第二次insert传入的是std::pair<const Foo, int>临时对象,其内部的Foo是const Foo类型的右值。const Foo右值无法绑定到Foo&&(移动构造函数的参数),因为无法移除const属性,因此只能匹配Foo(const Foo &)拷贝构造函数的参数,最终调用拷贝构造来创建节点中的Foo对象。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 11:32:03