nobar nobar - 2 months ago 10
C++ Question

How to initialize a map from a set

I want to set the keys of a

std::map
using the elements of a
std::set
(or a
std::vector
).

Something like the following...

std::set<int> keys = { 3,4,6 };
std::map<int,string> results(keys); // syntax error


Can this be done without explicitly iterating over the set?

Answer

You can't. A map is not a set. They're fundamentally different containers, even if the underlying structure is similar.


That said, anything's possible. The range constructor of std::map is linear time if the elements of the range are already sorted, which set guarantees for us. So all you need to do is apply a transformer to every element to produce a new range. The simplest would be to just use something like boost::make_transform_iterator (or roll your own):

template <class K, class F
    class V = decltype(std::declval<F&>()(std::declval<K const&>()))::second_type>
std::map<K, V> as_map(std::set<K> const& s, F&& f) {
    return std::map<K,V>(
        boost::make_transform_iterator(s.begin(), f),
        boost::make_transform_iterator(s.end(), f));
}

std::map<int,string> results =
    as_map(keys, [](int i){
        return std::make_pair(i, std::string{});
    });

which if you always will want default initialization, can just reduce to:

template <class V, class K>
std::map<K, V> as_map_default(std::set<K> const& s) {
    auto f = [](K const& k) { return std::make_pair(k, V{}); }
    return std::map<K,V>(
        boost::make_transform_iterator(s.begin(), f),
        boost::make_transform_iterator(s.end(), f)); 
}

std::map<int,string> results = as_map_default<string>(keys);