Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Initializing a map...

5 views
Skip to first unread message

barcaroller

unread,
Feb 20, 2008, 6:41:25 PM2/20/08
to

Is there a way in C++ to initialize an STL map in one statement (the way
arrays can be initialized in C)?

For example, instead of using:

map<type1,type2> mymap;
mymap[key1] = value1;
mymap[key2] = value2;


I would like to use something like:

// wrong syntax!
map<type1,type2> mymap = { (key1, value1), (key2, value2) };


Phil Endecott

unread,
Feb 20, 2008, 6:50:48 PM2/20/08
to

Boost.Assign has some stuff for this sort of thing.


Phil.

Jeff Schwab

unread,
Feb 20, 2008, 7:22:31 PM2/20/08
to

There's no special syntax for maps. You do have a few options, though.
One is to initialize an array with the nicer syntax, then initialize
the map from the array.

typedef std::map<type1, type2> map_type;
typedef map_type::value_type pair_type;

template<typename T, std::size_t z>
std::size_t size(T const (&a)[z]) {
return z;
}

int main() {
pair_type initializers[] =
{ pair_type(key1, value1), pair_type(key2, value2) };
map_type m(initializers, initializers + size(initializers));
}

Another option is to create the map within a function, then return it by
value.

map_type create_map() {
map_type result;
result.insert(pair_type(key1, value1));
result.insert(pair_type(key2, value2));
return result;
}

int main() {
map_type map = create_map();
}

A third option is to let the map start out empty, then use a function to
populate it.

void populate(map_type& m) {
m.insert(pair_type(key1, value1));
m.insert(pair_type(key2, value2));
}

int main() {
map_type m;
populate(m);
}

Sam

unread,
Feb 20, 2008, 7:26:27 PM2/20/08
to
barcaroller writes:

You can subclass it, and define an operator function.


template<typename keyType, typename valType> class myMap
: public std::map<keyType, valType> {

public:
myMap<keyType, valType> &operator()(keyType k, valType v)
{
(*this)[k]=v;
return *this;
};
};

You can initialize these objects as follows:

myMap<int, int> z=myMap<int, int>()(3, 4)(5, 6);

.. and so on. You can use these objects anywhere std::map is acceptable.


Jeff Schwab

unread,
Feb 20, 2008, 7:33:21 PM2/20/08
to

Augh! std::map is a concrete type, really not meant to be publicly
subclassed. It hasn't got a virtual destructor, for example, so the
following causes undefined behavior:

std::map* p = new myMap;
delete p;

Your idea is good, but either (1) the inheritance should be private, or
(2) myMap should contain the std::map sub-object as a member rather than
a base.

red floyd

unread,
Feb 20, 2008, 7:59:05 PM2/20/08
to

Or you could use a proxy initializer:


#include <map>

template<typename K, typename V>
class map_initializer_proxy
{
std::map<K, V> map_;

public:
map_initializer_proxy(const K& k, const V& v)
{
map_[k] = v;
}
map_initializer_proxy& operator()(const K& k, const V& v)
{
map_[k] = v;
return *this;
}
operator const std::map<K,V>&() const
{
return map_;
}
};
template<typename K, typename V>
map_initializer_proxy<K,V> map_initializer(const K& k, const V& v)
{
return map_initializer_proxy<K,V>(k,v);
}

#include <iostream>
#include <ostream>

int main()
{
std::map<int, int> m(map_initializer(3,4)(5,6)(7,8));
for (std::map<int, int>::iterator it = m.begin();
it != m.end();
++it)
std::cout << "( "
<< it->first
<< ","
<< it->second
<< " )"
<< std::endl;
return 0;
}


Jim Langston

unread,
Feb 20, 2008, 9:43:09 PM2/20/08
to
Your messages always have the text as an attachment instead of in the body
of the message. Just thought you might want to know.

--
Jim Langston
tazm...@rocketmail.com
"Sam" <s...@email-scan.com> wrote in message
news:cone.1203553586...@commodore.email-scan.com...


Jeff Schwab

unread,
Feb 20, 2008, 10:13:44 PM2/20/08
to
red floyd wrote:
> Jeff Schwab wrote:
>> Sam wrote:
>>> barcaroller writes:
>>>
>>>>
>>>> Is there a way in C++ to initialize an STL map in one statement (the
>>>> way arrays can be initialized in C)?
>>>>
>>>> For example, instead of using:
>>>>
>>>> map<type1,type2> mymap;
>>>> mymap[key1] = value1;
>>>> mymap[key2] = value2;
>>>>
>>>>
>>>> I would like to use something like:
>>>>
>>>> // wrong syntax!
>>>> map<type1,type2> mymap = { (key1, value1), (key2, value2) };
>>>
>>> You can subclass it, and define an operator function.
>>>
>>>
>>> template<typename keyType, typename valType> class myMap
>>> : public std::map<keyType, valType> {
>>>
>>> public:
>>> myMap<keyType, valType> &operator()(keyType k, valType v)
>>> {
>>> (*this)[k]=v;
>>> return *this;
>>> };
>>> };
>>>
>>> You can initialize these objects as follows:
>>>
>>> myMap<int, int> z=myMap<int, int>()(3, 4)(5, 6);
>>>
>>> .. and so on. You can use these objects anywhere std::map is acceptable.
...

> Or you could use a proxy initializer:
>
>
> #include <map>
>
> template<typename K, typename V>
> class map_initializer_proxy
> {
> std::map<K, V> map_;
>
> public:
> map_initializer_proxy(const K& k, const V& v)
> {
> map_[k] = v;
> }
> map_initializer_proxy& operator()(const K& k, const V& v)
> {
> map_[k] = v;
> return *this;
> }
> operator const std::map<K,V>&() const
> {
> return map_;
> }
> };
> template<typename K, typename V>
> map_initializer_proxy<K,V> map_initializer(const K& k, const V& v)
> {
> return map_initializer_proxy<K,V>(k,v);
> }
>
> #include <iostream>
> #include <ostream>
>
> int main()
> {
> std::map<int, int> m(map_initializer(3,4)(5,6)(7,8));
...
> return 0;
> }

That's a neat idea. It could probably be made a little more efficient
by replacing the calls to map::operator[] with calls to map::find and
map::insert. If the OP is willing to have two statements rather than
one, a copy of the map's data can also be saved by adding a swap function:

template<typename K, typename V>

struct map_initializer_proxy {

// ...

void swap(std::map<K, V>& m) {
map_.swap(m);
}
};


int main() {
std::map<int, int> m;
map_initializer(3,4)(5,6)(7,8).swap(m);
}

This is admittedly not as nifty, since it's not really an
initialization. It's more like a map_filler.

Alf P. Steinbach

unread,
Feb 20, 2008, 10:22:02 PM2/20/08
to
* Jim Langston:

> Your messages always have the text as an attachment instead of in the body
> of the message. Just thought you might want to know.
>

Only for Outlook Express users... ;-)

It seems Outlook Express is the only commonly used newsreader that
doesn't handle digitally signed messages correctly.


Cheers, & hth.,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

James Kanze

unread,
Feb 21, 2008, 4:39:08 AM2/21/08
to
On Feb 21, 1:22 am, Jeff Schwab <j...@schwabcenter.com> wrote:
> barcaroller wrote:
> > Is there a way in C++ to initialize an STL map in one
> > statement (the way arrays can be initialized in C)?

> > For example, instead of using:

> > map<type1,type2> mymap;
> > mymap[key1] = value1;
> > mymap[key2] = value2;

> > I would like to use something like:

> > // wrong syntax!
> > map<type1,type2> mymap = { (key1, value1), (key2, value2) };

> There's no special syntax for maps.

I think there will be in the next version of the standard. (I
know that there was a proposal for extended initializers, but
I'm not sure what the current status of the proposal was.)

> You do have a few options, though.

> One is to initialize an array with the nicer syntax, then initialize
> the map from the array.

This is the only way to create a const map.

> typedef std::map<type1, type2> map_type;
> typedef map_type::value_type pair_type;

> template<typename T, std::size_t z>
> std::size_t size(T const (&a)[z]) {
> return z;
> }

> int main() {
> pair_type initializers[] =
> { pair_type(key1, value1), pair_type(key2, value2) };
> map_type m(initializers, initializers + size(initializers));
> }

I often find it worthwhile to define a special structure for
this, something along the lines of:

typedef std::map< std::string, double > Map ;
struct MapInit
{
char const* key ;
double value ;
operator Map::value_type() const
{
return Map::value_type( std::string( key ), value ) ;
}
} ;

Partially for historical reasons: earlier Microsoft compilers
had problems with agglomerate initialization if the components
of the agglomerate weren't agglomerates themselves. But having
gotten into the habit of it... It's still worth considering if
the initialization array is in a separate compilation unit (e.g.
because it is machine generated), since you can arrange for the
table of MapInit to use static initialization, thus avoiding all
order of initialization issues.

> Another option is to create the map within a function, then
> return it by value.

> map_type create_map() {
> map_type result;
> result.insert(pair_type(key1, value1));
> result.insert(pair_type(key2, value2));
> return result;
> }

> int main() {
> map_type map = create_map();
> }

Not recommended for big maps in the middle of a tight loop:-).

> A third option is to let the map start out empty, then use a
> function to populate it.

> void populate(map_type& m) {
> m.insert(pair_type(key1, value1));
> m.insert(pair_type(key2, value2));
> }

> int main() {
> map_type m;
> populate(m);
> }

Neither of the above allow the map to be const, of course.

Another possibility is to derive, with the derived class
providing a constructor which populates the map (and nothing
else). Again, this allows the map to be const. It also works
with older implementations, which didn't always support the two
iterator form of the constructor (because they were designed
around compilers which didn't support member templates). So you
have something like:

class MyMap : public std::map< type1, type2 >
{
public:
MyMap() ;
} ;

MyMap::MyMap()
{
insert( value_type( key1, value1 ) ) ;
insert( value_type( key2, value2 ) ) ;
// ...
}

(Again, you could probably arrange for the constructor to be
machine generated.)

Concerning the "machine generated": if your map has so few
entries that you can consider writing them out by hand, you're
likely better off just using a C style array and std::find_if.
(For a const map, of course. But I can't remember a case where
I wanted to initialize the map's contents on creation, but
didn't want it const, although I'm sure that they do exist.)

--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

James Kanze

unread,
Feb 21, 2008, 4:52:10 AM2/21/08
to
On Feb 21, 1:33 am, Jeff Schwab <j...@schwabcenter.com> wrote:
> Sam wrote:
> > barcaroller writes:

> >> Is there a way in C++ to initialize an STL map in one
> >> statement (the way arrays can be initialized in C)?

> >> For example, instead of using:

> >> map<type1,type2> mymap;
> >> mymap[key1] = value1;
> >> mymap[key2] = value2;

> >> I would like to use something like:

> >> // wrong syntax!
> >> map<type1,type2> mymap = { (key1, value1), (key2, value2) };

> > You can subclass it, and define an operator function.

> > template<typename keyType, typename valType> class myMap
> > : public std::map<keyType, valType> {
> > public:
> > myMap<keyType, valType> &operator()(keyType k, valType v)
> > {
> > (*this)[k]=v;
> > return *this;
> > };
> > };

> > You can initialize these objects as follows:

> > myMap<int, int> z=myMap<int, int>()(3, 4)(5, 6);

Or even:

std::map< int, int > z = myMap< int, int >()( 3, 4 )( 5, 6 ) ;

I like it. I'm just afraid that it deviates enough from usual
practice to be a bit of obfuscation. (Maybe you should try to
get something like this into Boost, so that it can be considered
usual practice.)

Note too that as used above, there is a deep copy of the map.
Not necessarily the sort of thing you might want in a tight
loop. (But how often does one construct pre-initialized maps in
a tight loop, anyway?)

The other thing to consider is that it binds the initializer
list very tightly to the actual declaration, in a way which
makes machine generation of the initializers somewhat difficult.
And if the map is small enough that you're willing to consider
writing the initialers out by hand, it's probably small enough
for you to simply use find_if on a C style array.

> > .. and so on. You can use these objects anywhere std::map is
> > acceptable.

> Augh! std::map is a concrete type, really not meant to be
> publicly subclassed.

It's true that std::map isn't designed to be used as a base
class. But you have to weigh everything. It doesn't bother me
in the least to derive from it to provide a specialized
constructor which contains the specific initialization---the
semantics of the resulting class (with its specific
initialization) are such that no one is going to use it except
in its intended use. His class is more general, so perhaps the
risk is greater, but I still find it within the realm of
reasonableness. On the other hand...

std::map basically provides an implementation class for several
more or less distinct use cases. (See the discussions on its
operator[], which can't be used on a const object.) Perhaps the
ideal solution is to define precise interfaces for each of the
use cases, using a member std::map for the implementation.

> It hasn't got a virtual destructor, for example, so the
> following causes undefined behavior:

> std::map* p = new myMap;
> delete p;

I'd wonder about any code which allocated an std::map
dynamically to begin with. I don't think the risk here is very
great.

> Your idea is good, but either (1) the inheritance should be
> private, or (2) myMap should contain the std::map sub-object
> as a member rather than a base.

In both cases, this means a lot of brunt typing to duplicate the
interface. It might be worth it, however.

James Kanze

unread,
Feb 21, 2008, 4:58:24 AM2/21/08
to
On Feb 21, 4:22 am, "Alf P. Steinbach" <al...@start.no> wrote:
> * Jim Langston:

> > Your messages always have the text as an attachment instead
> > of in the body of the message. Just thought you might want
> > to know.

> Only for Outlook Express users... ;-)

Or Google groups.

> It seems Outlook Express is the only commonly used newsreader
> that doesn't handle digitally signed messages correctly.

For what definition of "correctly"? According to the RFC,
attachments are not allowed in news, so arguably, "correctly"
would mean not displaying or propagating the message:-).
(Realistically, of course: the first rule is always "be liberal
in what you accept, and conservative in what you send". No good
newsreader would ever append an attachment, but all good
newsreaders would accept it. And allowing at least a digital
signature does seem a reasonable extension to me.)

Alf P. Steinbach

unread,
Feb 21, 2008, 5:21:58 AM2/21/08
to
* James Kanze:

> On Feb 21, 4:22 am, "Alf P. Steinbach" <al...@start.no> wrote:
>> * Jim Langston:
>
>>> Your messages always have the text as an attachment instead
>>> of in the body of the message. Just thought you might want
>>> to know.
>
>> Only for Outlook Express users... ;-)
>
> Or Google groups.
>
>> It seems Outlook Express is the only commonly used newsreader
>> that doesn't handle digitally signed messages correctly.
>
> For what definition of "correctly"?

According to the RFC for signed messages, early 1990s IIRC.


> According to the RFC,

Wrong RFC, and it isn't an attachment: it's a signed message.


> attachments are not allowed in news, so arguably, "correctly"
> would mean not displaying or propagating the message:-).

Nope.


> (Realistically, of course: the first rule is always "be liberal
> in what you accept, and conservative in what you send". No good
> newsreader would ever append an attachment, but all good
> newsreaders would accept it. And allowing at least a digital
> signature does seem a reasonable extension to me.)

Agreed.

Sam

unread,
Feb 21, 2008, 7:10:39 AM2/21/08
to
Jeff Schwab writes:

>>
>> #include <iostream>
>> #include <ostream>
>>
>> int main()
>> {
>> std::map<int, int> m(map_initializer(3,4)(5,6)(7,8));
> ...
>> return 0;
>> }
>
> That's a neat idea. It could probably be made a little more efficient
> by replacing the calls to map::operator[] with calls to map::find and

operator[] takes only one argument. You can use a std::pair, but it'll make
this even more ugly.


Jeff Schwab

unread,
Feb 21, 2008, 1:37:20 PM2/21/08
to
Sam wrote:
> Jeff Schwab writes:
>
>>>
>>> #include <iostream>
>>> #include <ostream>
>>>
>>> int main()
>>> {
>>> std::map<int, int> m(map_initializer(3,4)(5,6)(7,8));
>> ...
>>> return 0;
>>> }
>>
>> That's a neat idea. It could probably be made a little more efficient
>> by replacing the calls to map::operator[] with calls to map::find and
>
> operator[] takes only one argument.

Right, but what's the relevance?

> You can use a std::pair, but it'll make this even more ugly.

I don't understand what you mean. I was pointing out that in:

m[k] = v;

The map first has to default-construct the value (assuming this is the
first time the map entry has been accessed), then assign to it. One
could instead check for the correct position of the value with
lower_bound (I mistakenly said map::find), then use that iterator as a
hint to insert the new value. This way, the value in the map can be
copy-constructed in the first place, rather than default-constructed,
then assigned.

Jeff Schwab

unread,
Feb 21, 2008, 1:48:54 PM2/21/08
to
James Kanze wrote:
> On Feb 21, 1:22 am, Jeff Schwab <j...@schwabcenter.com> wrote:
>> barcaroller wrote:
>>> Is there a way in C++ to initialize an STL map in one
>>> statement (the way arrays can be initialized in C)?
>
>>> For example, instead of using:
>
>>> map<type1,type2> mymap;
>>> mymap[key1] = value1;
>>> mymap[key2] = value2;
>
>>> I would like to use something like:
>
>>> // wrong syntax!
>>> map<type1,type2> mymap = { (key1, value1), (key2, value2) };
>
>> There's no special syntax for maps.
>
> I think there will be in the next version of the standard. (I
> know that there was a proposal for extended initializers, but
> I'm not sure what the current status of the proposal was.)

The proposal:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2215.pdf

I didn't know about that. Very neat. I'm not thrilled with g++ warning
me about:

std::tr1::array<int, 42> = { 0 }; // g++ wants { { 0 } }

It would bother me less if there were consistent syntax for primitive
and UD types.


>> You do have a few options, though.
>
>> One is to initialize an array with the nicer syntax, then initialize
>> the map from the array.
>
> This is the only way to create a const map.

That's a very good point, and is the reason my "not really
initialization" solutions are inferior.

>> int main() {
>> pair_type initializers[] =
>> { pair_type(key1, value1), pair_type(key2, value2) };
>> map_type m(initializers, initializers + size(initializers));
>> }
>
> I often find it worthwhile to define a special structure for
> this, something along the lines of:
>
> typedef std::map< std::string, double > Map ;
> struct MapInit
> {
> char const* key ;
> double value ;
> operator Map::value_type() const
> {
> return Map::value_type( std::string( key ), value ) ;
> }
> } ;

Is the key stored as a char const* so that construction of the
initializers does not require any run-time overhead? Does MapInit count
as a POD type, and is there benefit to using POD initializers?

James Kanze

unread,
Feb 22, 2008, 5:00:23 AM2/22/08
to
Jeff Schwab wrote:
> James Kanze wrote:

[...]


> > I often find it worthwhile to define a special structure for
> > this, something along the lines of:

> > typedef std::map< std::string, double > Map ;
> > struct MapInit
> > {
> > char const* key ;
> > double value ;
> > operator Map::value_type() const
> > {
> > return Map::value_type( std::string( key ), value ) ;
> > }
> > } ;

> Is the key stored as a char const* so that construction of the
> initializers does not require any run-time overhead? Does
> MapInit count as a POD type, and is there benefit to using POD
> initializers?

In this case, the key is stored as a char const* more by habit
than anything else. The underlying reason is that doing so
allows static initialization, which in turn avoids any possible
order of initialization issues. If the map and the table are
defined in the same file (which is the case here), the order of
initialization is well defined, and there is no problem. If the
initialization table is defined in another translation unit,
however (e.g. because it is machine generated), then static
initialization means that it is still guaranteed to work;
otherwise, you need some special logic to ensure that the table
is initialized before the map. Similarly, if you expect the map
to be used from constructors of other static objects, and use
the singleton idiom to construct it, unless the initialization
table uses static initialization, you might have an order of
initialization problem.

And of course, if the table is small enough to be initialized by
hand, I'll use std::find_if on it, rather than bother creating a
map. In such cases, ensuring static initialization also avoids
any order of initialization problems, without the need of a
singleton.

(FWIW: ensuring static initialization is practically the only
time I use char[] or char* instead of std::string.)

James Kanze

unread,
Feb 22, 2008, 5:07:34 AM2/22/08
to
On Feb 21, 7:37 pm, Jeff Schwab <j...@schwabcenter.com> wrote:
> Sam wrote:
> > Jeff Schwab writes:

> >>> #include <iostream>
> >>> #include <ostream>

> >>> int main()
> >>> {
> >>> std::map<int, int> m(map_initializer(3,4)(5,6)(7,8));
> >> ...
> >>> return 0;
> >>> }

> >> That's a neat idea. It could probably be made a little more efficient
> >> by replacing the calls to map::operator[] with calls to map::find and

> > operator[] takes only one argument.

> Right, but what's the relevance?

> > You can use a std::pair, but it'll make this even more ugly.

Any ugliness would be hidden in map_initializer, so who cares.

> I don't understand what you mean. I was pointing out that in:

> m[k] = v;

> The map first has to default-construct the value (assuming
> this is the first time the map entry has been accessed), then
> assign to it.

That's not really the issue---you're going to deep copy the map
anyway, so a little more or less shouldn't matter. The issue is
really one of semantics. Using insert or operator[] have
different semantics in the case of duplicates, and in the case
of insert, you can easily check the return value, and generate
an error (exception or assertion failure) in case of duplicates,
which is probably the most useful semantics.

> One could instead check for the correct position of the value
> with lower_bound (I mistakenly said map::find), then use that
> iterator as a hint to insert the new value.

You could, but I don't see what that buys you. Insertion
without the hint (or with the wrong hint) is O(ln n). With the
hint, it is O(1), but lower_bound is O(ln n), so there's no
point in using it just to provide a hint.

> This way, the value in the map can be copy-constructed in the
> first place, rather than default-constructed, then assigned.

Which could also be a very important advantage if one of the
types didn't support default construction.

All in all: forget about optimization---good design requires the
use of insert.

0 new messages