2

I am trying to access elements of a map data structure by key, but am getting a compiler error. I have defined my map data structure using typedefs to simplify the syntax for map instantiation. As you can see, the key is of type string and the data are custom GameComponent objects:

typedef map<string, GameComponent*> ComponentMap;
typedef map<string, GameComponent*>::iterator ComponentMapIter;
typedef map<string, GameComponent*>::const_iterator ComponentMapCIter;

In a derived class of GameComponent, I am creating standard Composite pattern methods along with accessors for each unique GameComponent object stored in my map. However, using the array subscript operator to access objects in accessors results in the compiler error:

void Character::add(const string& key, GameComponent* comp)
{
    m_components->insert( make_pair(key, comp) );
}

void Character::remove(const string& key)
{
    m_components->erase(key);
}

Armor* Character::getArmor() const
{
    // ERROR:
    return static_cast<Armor*>(m_components["Armor"]);
}

Weapon* Character::getWeapon() const
{
    // ERROR:
    return static_cast<Weapon*>(m_components["Weapon"]);
}

Attributes* Character::getAttributes() const
{
    // ERROR:
    return static_cast<Attributes*>(m_components["Attributes"]);
}

The output of the compiler error shows an "invalid type" error, which has me scratching my head:

/Users/Dylan/Desktop/RPG/character.cpp: In member function 'Armor* Character::getArmor() const':
/Users/Dylan/Desktop/RPG/character.cpp:66: error: invalid types 'ComponentMap* const[const char [6]]' for array subscript
/Users/Dylan/Desktop/RPG/character.cpp: In member function 'Weapon* Character::getWeapon() const':
/Users/Dylan/Desktop/RPG/character.cpp:71: error: invalid types 'ComponentMap* const[const char [7]]' for array subscript
/Users/Dylan/Desktop/RPG/character.cpp: In member function 'Attributes* Character::getAttributes() const':
/Users/Dylan/Desktop/RPG/character.cpp:76: error: invalid types 'ComponentMap* const[const char [11]]' for array subscript

3 Answers 3

6

It seems that m_components is of type ComponentMap*. When you write m_components["Armor"] compiler interprets that as an access to "Armor"-th element of dynamic array of ComponentMaps, which does not make any sense.

What you want is (*m_components)["some string"]. This will invoke operator[] of ComponentMap, but as Luchian Grigore and Olaf Dietsche mention, std::map::operator[] does not have a const overload, so this will fail too. The only option left is to use find.

The simplified edition will be:

Armor* Character::getArmor() const
{
    return static_cast<Armor*>(m_components->find("Armor")->second);
}

Weapon* Character::getWeapon() const
{
    return static_cast<Weapon*>(m_components->find("Weapon")->second);
}

Attributes* Character::getAttributes() const
{
    return static_cast<Attributes*>(m_components->find("Attributes")->second);
}

This code does not have the same behaviour as your original example and will fail if m_components does not have "Armor", "Weapon" and "Attributes" elements. The closest we can get is to explicitly handle absence of element and return 0 or nullptr if you use C++11.

Final correct C++03 compatible edition:

Armor* Character::getArmor() const
{
    ComponentMapCIter i = m_components->find("Armor");
    if (i != m_components->end())
        return static_cast<Armor*>(i->second);
    return 0;
}

Weapon* Character::getWeapon() const
{
    ComponentMapCIter i = m_components->find("Weapon");
    if (i != m_components->end())
        return static_cast<Weapon*>(i->second);
    return 0;
}

Attributes* Character::getAttributes() const
{
    ComponentMapCIter i = m_components->find("Attributes");
    if (i != m_components->end())
        return static_cast<Attributes*>(i->second);
    return 0;
}
Sign up to request clarification or add additional context in comments.

1 Comment

Yes, this is the one I ended up using. Thanks.
6

Since operator[] in a std::map is not const, you can't use it inside const methods (on members, of course).

Use at (C++11) or find & iterators pre-C++11.

Related: Why does std::map not have a const accessor?

Comments

2

getArmor(), getWeapon() and getAttributes() are defined const, but m_components[] might modify m_components. So you must either not define your methods const or use std::map::find instead.

Armor* Character::getArmor() const
{
    auto i = m_components->find("Armor");
    if (i != m_components->end())
        return static_cast<Armor*>(i->second);

    return nullptr;
}

5 Comments

Is auto a C++11 feature? It won't work using my version of the GNU compiler, which I think is pre-C++11...
@Dylan Just try it. It is already present with the -std=c++0x option. So if you have a reasonable current gcc (at least gcc 4.6), it will work.
@Dylan Just FYI, auto is already available with gcc-4.4 -std=c++0x (2009-04-21) and nullptr is available with gcc-4.6 -std=c++0x (2011-03-25).
Thanks, are these provided in specific header files?
@Dylan No header file is needed. When you add -std=c++0x to your command line (or -std=c++11 in case of gcc 4.7), auto and/or nullptr are available.

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.