Roger
Roger

Reputation: 657

Compile error: "no matching function for call to" when returning static_cast reference

I have this compile error no matching function for call to ‘Color::Color(Component&)’ on this templated method :

template<typename U>
inline U& getObject(const std::string& key)
  {
    return (static_cast<U>(*(_map.at(key))));
  }

I call it like that (components is of my type containing my map) :

components.getObject<Color>("Color").getColor();

Color inherit from Component and _map is a map of string and Component* (pointer on Component).

I have a lot of types derivating from Component and I store a pointer to them in my map. But when I try to get a Component from the map and cast it to a specific type to use it, I get the compile error above. I don't understand why.

Upvotes: 1

Views: 3637

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 598134

I have a lot of types derivating from Component and I store a pointer to them in my map. But when I try to get a Component from the map and cast it to a specific type to use it, I get the compile error above. I don't understand why.

Because your function is coded wrong.

Your function is retrieving a Component* pointer from the map, dereferencing the pointer to access just the Component portion of the object (not the full derived type), and is then trying to construct a temporary instance of the derived class with the Component as input.

In other words, your example is essentially doing this:

inline Color& getObject(const std::string& key)
{
    TComponent *pcomp = _map.at(key);
    Color tmp(*pcomp); // <-- error here
    return tmp;
}

The error means that your Color class does not have a constructor that accepts a Component as input, so it fails to compile.

Even if the code did compile, it would still be wrong, because you are returning a reference to a temporary object that gets destroyed when the function exits, so the caller will end up with a dangling reference to invalid memory.

As @MikeVine stated in a comment, you can write the function like this to fix it:

template<typename U>
inline U& getObject(const std::string& key)
{
    return static_cast<U&>(*(_map.at(key)));
}

And that would work, as it eliminates the construction of a temporary object, and returns a reference to the existing object stored in the map.

You could also write the function like this instead:

template <typename U>
inline U& getObject(const std::string& key)
{
    return *(static_cast<U*>(_map.at(key)));
}

This code casts the Component* pointer to a U* pointer first before then dereferencing it to access the derived class object.

If you are not sure at runtime whether the requested component is actually the requested type, you could add some extra error handling to make sure before returning it:

template <typename U>
inline U& getObject(const std::string& key)
{
    U *ptr = dynamic_cast<U*>(_map.at(key));
    if (!ptr) throw std::invalid_argument("");
    return *ptr;
}

Alternatively:

template<typename U>
inline U& getObject(const std::string& key)
{
    return dynamic_cast<U&>(*(_map.at(key)));
}

Upvotes: 3

Related Questions