简体   繁体   中英

Setting all values in a std::map

如何将std::map中的所有值设置为相同的值,而不使用循环遍历每个值?

Using a loop is by far the simplest method. In fact, it's a one-liner: [C++17]

for (auto& [_, v] : mymap) v = value;

Unfortunately C++ algorithm support for associative containers isn't great pre-C++20. As a consequence, we can't directly use std::fill .

To use them anyway (pre-C++20), we need to write adapters — in the case of std::fill , an iterator adapter. Here's a minimally viable (but not really conforming) implementation to illustrate how much effort this is. I do not advise using it as-is. Use a library (such as Boost.Iterator ) for a more general, production-strength implementation.

template <typename M>
struct value_iter : std::iterator<std::bidirectional_iterator_tag, typename M::mapped_type> {
    using base_type = std::iterator<std::bidirectional_iterator_tag, typename M::mapped_type>;
    using underlying = typename M::iterator;
    using typename base_type::value_type;
    using typename base_type::reference;

    value_iter(underlying i) : i(i) {}

    value_iter& operator++() {
        ++i;
        return *this;
    }

    value_iter operator++(int) {
        auto copy = *this;
        i++;
        return copy;
    }

    reference operator*() { return i->second; }

    bool operator ==(value_iter other) const { return i == other.i; }
    bool operator !=(value_iter other) const { return i != other.i; }

private:
    underlying i;
};

template <typename M>
auto value_begin(M& map) { return value_iter<M>(map.begin()); }

template <typename M>
auto value_end(M& map) { return value_iter<M>(map.end()); }

With this, we can use std::fill :

std::fill(value_begin(mymap), value_end(mymap), value);

I encountered the same problem but found that the range returned by boost::adaptors::values is mutable, so it can then be used with normal algorithms such as std::fill.

#include <boost/range/adaptor/map.hpp>
auto my_values = boost::adaptors::values(my_map);
std::fill(my_values.begin(), my_values.end(), 123);

The boost::assign library has all sorts of neat stuff to help out initializing the contents of a container. My thought that this could be used to avoid explicitly iterating through the map. Unfortunately, maps are curious beasts difficult to initialize because the keys must be unique. The bottom line is that a simple for loop is probably the best way to initialize a map. It may not be super elegant, but it gets the job done and is immediatly comprehensible by anyone with any acquaintance with the STL.

map <int,string> myMap;
for( int k=0;k<1000;k++)
  myMap.insert(pair<int,string>(k,string("")));

The rest of this post describes the journey I took to reach the above conclusion.

The boost::assign makes it simple to assign a small number of values to a map.

map<string,int> m; 
insert( m )( "Bar", 1 )( "Foo", 2 );

or

 map<int,int> next = map_list_of(1,2)(2,3)(3,4)(4,5)(5,6);

In your case, where you want to initialize the entire map with the same value, there are the utilities repeat and repeat_fun.
Something like this should work with a multimap ( untested code snippet )

pair<int,string> init( 0,string(""));
multimap <int,string> myMap = repeat(1000,init);

As Konrad Rudolph as pointed out, you cannot initialize a map with the same exact value, because the keys must be unique.

This makes life much more complex ( fun? ). Something like this, perhaps:

map <int,string> myMap;

struct nextkey
{
   int start;
   nextkey( s ) : start( s ) {}
   pair<int,string> operator () ()
{
   return pair<int,string>(start++,string(""));
}
};

myMap = repeat_fun(1000,nextkey(0));

Now, this is getting so complex, I now think a simple iteration IS the way to go

map <int,string> myMap;
for( int k=0;k<1000;k++)
  myMap.insert(pair<int,string>(k,string("")));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM