1

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.

4 Answers 4

10

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;
};
Sign up to request clarification or add additional context in comments.

8 Comments

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?
Yes of course, this means then that the Assignment Operator would not work :)
The compiler would probably optimize the copy away and directly build it in the attribute.
Glad I could provide a simple solution :)
Best solution, +1. @Steve: the compiler (well, all modern compilers) does optimize by eliding the unnecessary copy here. So this is really inexpensive.
|
4

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.

Comments

3

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.

4 Comments

Given the last sentence from constructor parameters, I would say you miss the point... As I did in my first attempt.
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?
For the sake of simplicity, I'd say build your map in the constructor body.
@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).
2

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!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.