Initializing a map...

Discussion in 'C++' started by barcaroller, Feb 20, 2008.

  1. barcaroller

    barcaroller Guest

    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) };
     
    barcaroller, Feb 20, 2008
    #1
    1. Advertising

  2. 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) };


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


    Phil.
     
    Phil Endecott, Feb 20, 2008
    #2
    1. Advertising

  3. barcaroller

    Jeff Schwab Guest

    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. 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);
    }
     
    Jeff Schwab, Feb 21, 2008
    #3
  4. barcaroller

    Sam Guest

    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.



    -----BEGIN PGP SIGNATURE-----
    Version: GnuPG v1.4.7 (GNU/Linux)

    iD8DBQBHvMUyx9p3GYHlUOIRAr9AAJ0Z1htKkfWKFdWGK/Qi6WDcGYNA3ACbB7mO
    8LABhHnbT/QP8M2HfUwxzxM=
    =4zSY
    -----END PGP SIGNATURE-----
     
    Sam, Feb 21, 2008
    #4
  5. barcaroller

    Jeff Schwab Guest

    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.


    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.
     
    Jeff Schwab, Feb 21, 2008
    #5
  6. barcaroller

    red floyd Guest

    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.

    >
    > 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:
    >
    >


    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;
    }
     
    red floyd, Feb 21, 2008
    #6
  7. barcaroller

    Jim Langston Guest

    Re: Initializing a map... [OT] to sam

    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

    "Sam" <> wrote in message
    news:-scan.com...
     
    Jim Langston, Feb 21, 2008
    #7
  8. barcaroller

    Jeff Schwab Guest

    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::eek:perator[] 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.
     
    Jeff Schwab, Feb 21, 2008
    #8
  9. Re: Initializing a map... [OT] to sam

    * 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?
     
    Alf P. Steinbach, Feb 21, 2008
    #9
  10. barcaroller

    James Kanze Guest

    On Feb 21, 1:22 am, Jeff Schwab <> 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:
    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, Feb 21, 2008
    #10
  11. barcaroller

    James Kanze Guest

    On Feb 21, 1:33 am, 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);


    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 (GABI Software) email:
    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, Feb 21, 2008
    #11
  12. barcaroller

    James Kanze Guest

    Re: Initializing a map... [OT] to sam

    On Feb 21, 4:22 am, "Alf P. Steinbach" <> 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.)

    --
    James Kanze (GABI Software) email:
    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, Feb 21, 2008
    #12
  13. Re: Initializing a map... [OT] to sam

    * James Kanze:
    > On Feb 21, 4:22 am, "Alf P. Steinbach" <> 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.


    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?
     
    Alf P. Steinbach, Feb 21, 2008
    #13
  14. barcaroller

    Sam Guest

    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::eek:perator[] 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.



    -----BEGIN PGP SIGNATURE-----
    Version: GnuPG v1.4.7 (GNU/Linux)

    iD8DBQBHvWo7x9p3GYHlUOIRAnzkAJ9rd3VYOlnl6mk9f6Nk2mVwbrt4/QCbBVn+
    wvGArnNFpaAb6SHW9LJnAJU=
    =2p1k
    -----END PGP SIGNATURE-----
     
    Sam, Feb 21, 2008
    #14
  15. barcaroller

    Jeff Schwab Guest

    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::eek:perator[] 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, Feb 21, 2008
    #15
  16. barcaroller

    Jeff Schwab Guest

    James Kanze wrote:
    > On Feb 21, 1:22 am, Jeff Schwab <> 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?
     
    Jeff Schwab, Feb 21, 2008
    #16
  17. barcaroller

    James Kanze Guest

    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 (GABI Software) email:
    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, Feb 22, 2008
    #17
  18. barcaroller

    James Kanze Guest

    On Feb 21, 7:37 pm, Jeff Schwab <> 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::eek:perator[] 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.

    --
    James Kanze (GABI Software) email:
    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, Feb 22, 2008
    #18
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. utab
    Replies:
    2
    Views:
    1,042
    Aman Angrish
    May 15, 2006
  2. Replies:
    3
    Views:
    444
    Victor Bazarov
    Jun 14, 2006
  3. Marc D
    Replies:
    2
    Views:
    426
    Marc D
    Feb 14, 2007
  4. Rhino
    Replies:
    17
    Views:
    4,176
    Roedy Green
    Mar 20, 2010
  5. Mike Copeland

    Initializing std::map Objects

    Mike Copeland, Jun 23, 2013, in forum: C++
    Replies:
    9
    Views:
    278
    James Kanze
    Jun 25, 2013
Loading...

Share This Page