Search code examples
c++stdvectorstdmap

How to insert elements in std::vector<std::map<int, std::unique_ptr<int>>> container?


I have got std::vector<std::map<int, std::unique_ptr<int>>> container (if to simplify). Initially, I have to insert in std::vector some amount of std::map, each of them will have one key-value pair. I tried such code:

#include <iostream>
#include <map>
#include <vector>
#include <memory>

using namespace std;

int main()
{
    vector<map<int, std::unique_ptr<int>>> data{};

    for (int x = 0; x < 10; x++)
    {
        data.emplace_back(std::make_pair(x, std::make_unique<int>(x)));
    }
}

But it did not work. How can I change it to make it work as expected?

EDIT: I understood my mistake, I had to use this piece of code to add th element:

std::map<int, std::unique_ptr<int>> m;
m.emplace(x, std::make_unique<int>(y));
data.emplace_back(std::move(m));

Then, the code will work in online compilers, but it still does not work in Visual Studio.


Solution

  • The cause of your error is that emplace_back will attempt to construct a map in the vector like:

    std::map<int, std::unique_ptr<int>>(std::make_pair(...))
    

    See also: std::map constructors

    However, this is not possible, because the constructor which takes pairs is taking std::initializer_list<std::pair<K, V>>. There is no constructor that takes a single pair. The correct way is:

    map<int, unique_ptr>{std::make_pair(...)}
    

    However, constructors taking std::initializer_list<T> require T to be copyable, and std::unique_ptr<int> is not copyable, so the containing T = std::pair<int, std::unique_ptr<int>> also isn't copyable. This leaves us with the following solution:

    #include <iostream>
    #include <map>
    #include <vector>
    #include <memory>
    
    int main()
    {
        using map_type = std::map<int, std::unique_ptr<int>>;
    
        std::vector<map_type> data{};
    
        for (int x = 0; x < 10; x++)
        {
            for (int y = 0; y < 10; y++)
            {
                map_type m;
                m.emplace(x, std::make_unique<int>(y));
                data.emplace_back(std::move(m));
            }
        }
    }
    

    See live example

    Visual Studio Issues

    In MSVC's standard library, it is impossible to use a std::vector<std::map<int, std::unique_ptr<int>>. The reason is that due to strong exception guarantees, std::vector requires its element type to be std::nothrow_move_constructible, or copyable.

    You can see that in the MSVC STL, std::map has non-noexcept move constructor:

    map(map&& _Right) : _Mybase(_STD move(_Right)) {}
    

    Here is a quick and dirty fix for this issue:

    template <typename K, typename V>
    struct nothrow_map : std::map<K, V> {
        nothrow_map() = default;
        nothrow_map(nothrow_map&& other) noexcept : std::map<K, V>(std::move(other)) {}
    };
    
    // ...
    
    using map_type = nothrow_map<int, std::unique_ptr<int>>;
    

    See also: std::move_if_noexcept (used by std::vector internally)