std::map::operator[]比std::map::insert更有效率?

std::map::operator[] is more efficient than std::map::insert?

我了解到 operator[] 等同于

(*((this->insert(make_pair(k,mapped_type()))).first)).second

因此,如果映射中不存在键,使用 operator[] 的效率低于使用 insert,对吗?因为还有一步,默认构造。

所以我写了一个demo来测试,像这样。


#include <iostream>
#include <map>

using namespace std;

static int id = 0;

class Foo
{
public:
    Foo()
        : _val(0)
    {
        _id = ++id;
        cout << _id << " construct_no_param:" << _val << endl;
    }
    Foo(int val)
        : _val(val)
    {
        _id = ++id;
        cout << _id << " construct_has_param:" << _val << endl;
    }
    Foo(Foo &rhs)
        : _val(rhs._val)
    {
        _id = ++id;
        cout << _id << " construct_copy:" << _val << endl;
    }
    Foo(const Foo &rhs)
        : _val(rhs._val)
    {
        _id = ++id;
        cout << _id << " construct_const_copy:" << _val << endl;
    }
    Foo &operator=(Foo &rhs)
    {
        _val = rhs._val;
        cout << _id << " assign:" << _val << endl;
    }
    Foo &operator=(const Foo &rhs)
    {
        _val = rhs._val;
        cout << _id << " const_assign:" << _val << endl;
    }
    ~Foo()
    {
        cout << _id << " destruct:" << _val << endl;
    }

    int _val;
    int _id;
};

int main() {
    map<int, Foo> m;
    const Foo f(2);
    cout << "-----" << endl;
    // m[1] = f;
    // m.insert(make_pair(1, f));
    cout << "-----" << endl;
    return 0;
}

首先,我使用了运算符[]

    m[1] = f;

我得到了这个:

1 construct_has_param:2
-----
2 construct_no_param:0
3 construct_const_copy:0
4 construct_const_copy:0
3 destruct:0
2 destruct:0
4 assign:2
-----
1 destruct:2
4 destruct:2

然后,我用了insert

    m.insert(make_pair(1, f));

我得到了这个:

1 construct_has_param:2
-----
2 construct_const_copy:2
3 construct_const_copy:2
4 construct_const_copy:2
5 construct_const_copy:2
4 destruct:2
3 destruct:2
2 destruct:2
-----
1 destruct:2
5 destruct:2

结果与我的预期不符。为什么 insert 有更多的结构?这是怎么回事?

顺便说一句,我使用 -O0 选项来禁用优化,但我不确定它是否有效。

gcc 版本 4.8.2 20131212(红帽 4.8.2-8)(GCC)

表达式 make_pair(1, f) 的类型为 std::pair<int, Foo>,这不是您的映射 value_type,而是 std::pair<const int, Foo>,因此 std::pair<const int, Foo> 的参数中有一个副本=16=]。然后 map 内有 2 个副本,如果您的所有特殊成员没有抑制移动构造的生成,这将是移动。

如果您想尽可能避免复制,请使用 emplace

m.emplace(1, f);

并定义Foo的移动构造函数和赋值

Foo(Foo &&rhs)
    : _val(std::exchange(rhs.val, 0))
{
    _id = ++id;
    cout << _id << " move:" << _val << endl;
}

Foo& opeator=(Foo &&rhs)
{
    std::swap(_val, rhs._val);
    cout << _id << " move assign:" << _val << endl;
}

我还建议放弃 non-const 复制操作。