What is the preferred/idiomatic way to insert into a map?

前端 未结 9 1487
感动是毒
感动是毒 2020-11-28 18:45

I have identified four different ways of inserting elements into a std::map:

std::map function;

function[0] = 42;
function.inse         


        
9条回答
  •  天涯浪人
    2020-11-28 19:05

    Since C++17 std::map offers two new insertion methods: insert_or_assign() and try_emplace(), as also mentioned in the comment by sp2danny.

    insert_or_assign()

    Basically, insert_or_assign() is an "improved" version of operator[]. In contrast to operator[], insert_or_assign() doesn't require the map's value type to be default constructible. For example, the following code doesn't compile, because MyClass does not have a default constructor:

    class MyClass {
    public:
        MyClass(int i) : m_i(i) {};
        int m_i;
    };
    
    int main() {
        std::map myMap;
    
        // VS2017: "C2512: 'MyClass::MyClass' : no appropriate default constructor available"
        // Coliru: "error: no matching function for call to 'MyClass::MyClass()"
        myMap[0] = MyClass(1);
    
        return 0;
    }
    

    However, if you replace myMap[0] = MyClass(1); by the following line, then the code compiles and the insertion takes place as intended:

    myMap.insert_or_assign(0, MyClass(1));
    

    Moreover, similar to insert(), insert_or_assign() returns a pair. The Boolean value is true if an insertion occurred and false if an assignment was done. The iterator points to the element that was inserted or updated.

    try_emplace()

    Similar to the above, try_emplace() is an "improvement" of emplace(). In contrast to emplace(), try_emplace() doesn't modify its arguments if insertion fails due to a key already existing in the map. For example, the following code attempts to emplace an element with a key that is already stored in the map (see *):

    int main() {
        std::map> myMap2;
        myMap2.emplace(0, std::make_unique(1));
    
        auto pMyObj = std::make_unique(2);    
        auto [it, b] = myMap2.emplace(0, std::move(pMyObj));  // *
    
        if (!b)
            std::cout << "pMyObj was not inserted" << std::endl;
    
        if (pMyObj == nullptr)
            std::cout << "pMyObj was modified anyway" << std::endl;
        else
            std::cout << "pMyObj.m_i = " << pMyObj->m_i <<  std::endl;
    
        return 0;
    }
    

    Output (at least for VS2017 and Coliru):

    pMyObj was not inserted
    pMyObj was modified anyway

    As you can see, pMyObj no longer points to the original object. However, if you replace auto [it, b] = myMap2.emplace(0, std::move(pMyObj)); by the the following code, then the output looks different, because pMyObj remains unchanged:

    auto [it, b] = myMap2.try_emplace(0, std::move(pMyObj));
    

    Output:

    pMyObj was not inserted
    pMyObj pMyObj.m_i = 2

    Code on Coliru

    Please note: I tried to keep my explanations as short and simple as possible to fit them into this answer. For a more precise and comprehensive description, I recommend reading this article on Fluent C++.

提交回复
热议问题