c++buildcompilationinitializer

std::map insert doesn't compile due to "cannot convert argument 1 from 'std::pair<MyKey, MyValue>' to 'std::pair<const _Kty,_Ty> &&'"


I'm trying to create an std::map for <MyKey, MyValue>. MyKey is an enum, MyValue an external class.

Calling myMap.insert({ key, value }) always fails the compile with error

"cannot convert argument 1 from 'std::pair<MyKey, MyValue>' to 'std::pair<const _Kty,_Ty> &&'"

Although primitive data types always work with std::map.insert(), this problem happens so often when trying to contain classes written by someone else. With different third-party classes, I've tried many workarounds such as constructing objects beforehand or setting their attributes after insertion. But I've yet to find a systematic way to fix this. It seems std::map is much harder to get right than, say, python's dict.

Example: with the thirdparty lib cppzmq, Visual Studio 2017

enum MyKey {
    key1,
    key2
}

std::map<MyKey, zmq::socket_t> mymap;

std::shared_ptr<zmq::context_t> g_context = std::make_shared<zmq::context_t>(1);

zmq:socket_t sock(*g_context, zmq::socket_type::pair);

mymap.insert({ key1, sock });

gives me the above error.

What does this error mean and how to fix it in general?


Solution

  • If you want to insert a move-only object into a std::map then your only option is to move it into map. Adapting your example, you could try this:

    mymap.insert({key1, zmq:socket_t{*g_context, zmq::socket_type::pair}});
    

    You could also do this:

    zmq:socket_t sock{*g_context, zmq::socket_type::pair};
    mymap.insert({key1, std::move(sock)});
    // note that sock is in a "moved from" state after this point
    

    I see that you're coming from Python and might not be familiar with move semantics so the answers to this question might be helpful.