TransWikia.com

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

Stack Overflow Asked by EddieIpeace on December 27, 2020

I’ve learnt that operator[] is equivalent to

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

So if a key doesn’t exist in the map, using operator[] is less efficient than using insert, right? Because there’s one more step, default construct.

So I wrote a demo to test, like this.


#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;
}

First, I used operator[]

    m[1] = f;

And I got this:

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

Then, I used insert

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

And I got this:

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

The results are not correspond to what I’m expected. Why insert has more constructions? What’s going on here?

BTW, I use -O0 option to disable optimization, but I’m not sure if it works.

gcc version 4.8.2 20131212 (Red Hat 4.8.2-8) (GCC)

One Answer

The expression make_pair(1, f) has type std::pair<int, Foo>, which isn't your maps value_type, which is std::pair<const int, Foo>, so there is a copy in the arguments of insert. There then are 2 copies within map, that would be moves if all your special members didn't suppress the generation of move construction.

If you want to avoid as much copying as you can, use emplace

m.emplace(1, f);

and define Foo's move constructor and assignment

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;
}

I would also recommend dropping the non-const copy operations.

Correct answer by Caleth on December 27, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP