C++ How to add objects to maps and return reference to new created object inside map

3.9k Views Asked by At

Bellow I provide the complete code for something really simple which I'm struggling with..

  • I need to create a map with strings and Objects...
  • When requested, if the string is inside the map, I need to return a reference to one object inside the map
  • When the string is not inside the map, I need to create that object, with that string and return (as before) the reference to the new created object

Please check below the two comments I say "ERROR" to see where the problem is.

My questions are:

  1. How can I insert to a map, one object? what is wrong with the line on the InitObj()?
  2. How can I create return a reference to an object which I've just created on a map? As seen at the end of getHouse() function

Thanks in advance :)

#include <map>
#include <string>
#include <memory>

class House
{
public:

    House(const char* name) : _name(name) {};
    ~House() {};

    std::string getHouseName () { return _name; }
private:
    std::string _name;

    House(const House& copy)
    {
    }
    House& operator=(const House& assign)
    {
    }
};

class Obj
{
public:
    Obj()
    {
        InitObj();
    }
    ~Obj() {};

    House& getHouse (const char *houseName)
    {
        std::string name = houseName;
        auto i = _myHouseMap.find(name);

        //this string doesn't exist on map? then create a new house and add to the map and return the reference to it
        if (i == _myHouseMap.end())
        {
            //create a new house
            House h(houseName); 

            //add to the map
            _myHouseMap.insert(std::pair<const std::string, House>(houseName, h));

            //return the reference to the house created
            return h; //<--- ERROR!!!! need to return the reference!
        }
        return (i->second);
    }



private:
    Obj(const Obj& copy);
    Obj& operator=(const Obj& assign);

    typedef std::map<const std::string, House> myHouseMap;

    myHouseMap _myHouseMap;

    //the initialization will add one object to my map
    void InitObj()
    {
        House h("apartment");
        _myHouseMap.insert(std::pair<const std::string, House>("apartment", h)); //<--- ERROR see reference to function template instantiation 'std::pair<_Ty1,_Ty2>::pair<const char(&)[10],House&>
    }
};


int main(void)
{
    Obj aaa;

    House& myHouse1 = aaa.getHouse ("apartment");
    std::cout << "House is " << myHouse1.getHouseName ();

    House& myHouse2 = aaa.getHouse ("newHouse"); //here a new house must be created and added to the map
    std::cout << "House is " << myHouse2.getHouseName ();

    return 0;
}
1

There are 1 best solutions below

2
Barry On BEST ANSWER

For your first question, you made House noncopyable (your copy constructor and copy assignment operator are private). The approach you are taking to insert requires you to make a pair first, the construction of which will copy the House you pass in. If you have access to a C++11 compiler, you can still have the value-type of your map be House and just use emplace instead:

void InitObj()
{
    _myHouseMap.emplace(std::piecewise_construct,
                        std::forward_as_tuple("apartment"),  //key
                        std::forward_as_tuple("apartment")); //value
}    

If you don't have access to a C++11 compiler, you will have to change the value type to be House* or some equivalent copy-constructible type.

For the second question, std::map::insert (and emplace) return a pair<iterator, bool>. Just take advantage of that:

if (i == _myHouseMap.end())
{
    House h(houseName); 

    // we insert our new house into the map
    // insert() will return a pair<iterator, bool>. 
    // the bool will be true if the insert succeeded - which we know
    // it will because we know that this key isn't already in the map
    // so we just reassign 'i' to be insert().first, the new iterator
    // pointing to the newly inserted element
    i = _myHouseMap.insert(std::pair<const std::string, House>(houseName, h)).first;
}

// here i either points to the element that was already in the map
// or the new element that we just inserted. either way, 
// we want the same thing
return i->second;