views:

621

answers:

4

I was wondering if this was possible, and if so how I'd go about doing so. If it's not possible I'll just have to add the elements during the constructor body.

Ideally I would like the map immutable after construction.

What I'm trying to achieve is adding two pairs to the map that are created from constructor parameters.

+9  A: 

It's possible, by copy construction: invoke a function which build the map!

std::map<int,int> myFunc(int a, int b);

class MyObject
{
public:
  MyObject(int a, int b): m_map(myFunc(a,b)) {}

private:
  std::map<int,int> m_map;
};
Matthieu M.
This could be fairly clean. Would this allow me to then make my newly constructed map constant as it's being defined at initialization time?
Brian
Yes of course, this means then that the Assignment Operator would not work :)
Matthieu M.
+1 - Nice, but couldn't it be quite expensive to copy-cstr if the map is big ?
Steve Schnepp
The compiler would probably optimize the copy away and directly build it in the attribute.
Matthieu M.
2 element map of effectively int to int mappings. Really small
Brian
Works flawlessly in my tests. Very glad to make this map a const
Brian
Glad I could provide a simple solution :)
Matthieu M.
Best solution, +1. @Steve: the compiler (well, all modern compilers) *does* optimize by eliding the unnecessary copy here. So this is really inexpensive.
Konrad Rudolph
+4  A: 

Without extra tools you can only init std containers in initialization lists as far as their constructors support you. If you need more, map_list_of() et al from Boost.Assign do a great job.

Boost.Assign in action:

class C
{
    const std::map<int,int> _vi;
public:
    C() : _vi(boost::assign::map_list_of(1,1)(2,1)(3,2)) {}
}

edit: updated to std::map example.

Georg Fritzsche
Of course, the OP would use `map_list_of` instead.
rlbond
For some strange reason i overread the map part.
Georg Fritzsche
+3  A: 

There’s a map constructor that takes a range as a pair of iterators. Using that, you can construct something similar to your demands:

pair<int, int> init[] = { make_pair(1, 2), make_pair(2, 3) };
map<int, int> const mymap(init, init + 2);

Granted, not pretty. The next version of C++ will come with better support for initialization lists. Until then, Boost.Assign is the next best thing.

Konrad Rudolph
Given the last sentence `from constructor parameters`, I would say you miss the point... As I did in my first attempt.
Matthieu M.
So it sounds like if I don't want to have fairly complex iterator arithmetic in my initialization block and don't have the option of using boost I'm relegated to the constructor body?
Brian
No you are not, copy construction works just fine.
Matthieu M.
For the sake of simplicity, I'd say build your map in the constructor body.
navigator
@Mathieu: granted, that’s slightly more complicated syntactically but it still works essentially the same way (at least if the two pairs are statically available, i.e. can be initialized as a static member of the class).
Konrad Rudolph
+1  A: 

I use an initializer class:

template<class K, class V>
class MapInitializer
{
    std::map<K,V> m;
public:
    operator std::map<K,V>() const 
    { 
        return m; 
    }

    MapInitializer& Add( const K& k, const V& v )
    {
        m[ k ] = v;
        return *this;
    }
};

Then to put it to use:

class C
{
    const std::map<int,const char*> m_map;

public:        
    C() : m_map( MapInitializer<int,const char*>()
      .Add( 1, "Hi there" )
      .Add( 2, "In the middle" )
      .Add( 9, "Goodbye" ) )
    {}
};

This is free (in the sense that you aren't building a map, copying it, and throwing the first away) because of C++'s return value optimization. The same thing can be done for initializing a vector or other standard containers.

Hope that helps!

mlimber