Rick
Rick

Reputation: 451

Can I emplace a value into a vector that is stored in a map in C++?

I was wondering if it's possible to emplace a value into a vector that is stored in a map.

Currently I do this like so...

std::map<std::string, std::vector<std::string>> my_collection;
my_collection["Key"].push_back("MyValue");

I was thinking I could do the following and C++ would be smart enough to realize it should add it to the vector... but I get a memory compile error.

my_collection.emplace("Key", "MyValue");

Upvotes: 2

Views: 882

Answers (2)

ypnos
ypnos

Reputation: 52357

You can create a vector, emplace into it, and then move the vector. This way your object will neither be copied or moved:

std::map<std::string, std::vector<std::string>> my_collection;
std::vector<std::string> temp;
temp.emplace_back("MyValue");
my_collection["Key"] = std::move(temp);

Alternatively, you create the vector in the map and work on a reference:

std::map<std::string, std::vector<std::string>> my_collection;
auto &keyVec = my_collection["Key"];
keyVec.emplace_back("MyValue");

Conveniently, this boils down to:

std::map<std::string, std::vector<std::string>> my_collection;
my_collection["Key"].emplace_back("MyValue");

Upvotes: 2

bolov
bolov

Reputation: 75727

No matter how smart C++ gets, it still has to respect the language rules and the public interfaces. std::map does have an emplace member. You need to use that.

The problem is that there is no way to construct a vector by moving elements into it (because of how std::initializer_list is designed - don't get me started)

If you don't care about that and you can accept copying elements into the vector then all you need to do is:

auto test()
{
    using namespace std::string_literals;

    std::map<std::string, std::vector<std::string>> my_collection;

    my_collection.emplace("key"s, std::vector{"MyValue"s});
}

The above will copy "MyValue"s into a vector then will move the key and the vector into the map.

However if you do want moves or if you have move-only types then there is some extra work.

So I create a little utility function which does that: creates a vector by moving rvalues passed to it:

template <class... Args>
auto make_vector(Args&&... args)
{
    using T = std::remove_reference_t<std::common_type_t<Args...>>;
    static_assert((... && std::is_same_v<T, std::remove_reference_t<Args>>));

    auto v = std::vector<T>{};
    v.reserve(sizeof...(Args));
    (..., v.emplace_back(std::forward<Args>(args))); 

    return v;
}

auto test()
{
    using namespace std::string_literals;

    std::map<std::string, std::vector<std::string>> my_collection;

    my_collection.emplace("key", make_vector("MyValue"s));
}

Upvotes: 0

Related Questions