Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is such a downcast safe?

Tags:

c++

Suppose we have the following code:

#include <memory>
#include <vector>

struct BaseComponent
{
    template <typename T>
    T * as()
    {
        return static_cast<T*>(this);
    }

    virtual ~BaseComponent() {}
};

template <typename T>
struct Component : public BaseComponent
{
    virtual ~Component() {}
};

struct PositionComponent : public Component<PositionComponent>
{
    float x, y, z;

    virtual ~PositionComponent() {}
};

int main()
{
    std::vector<std::unique_ptr<BaseComponent>> mComponents;
    mComponents.emplace_back(new PositionComponent);

    auto *pos = mComponents[0]->as<PositionComponent>();
    pos->x = 1337;

    return 0;
}

In the T * as() method, should I use a static_cast or a dynamic_cast? are there times when the the conversion will fail? Do I need to dynamic_cast like this instead?

    auto *ptr = dynamic_cast<T*>(this);

    if(ptr == nullptr)
        throw std::runtime_error("D'oh!");

    return ptr;
like image 482
Simon G. Avatar asked Mar 22 '23 19:03

Simon G.


2 Answers

In your case there is no way to tell statically whether this is the right type or not. What you may want is a CRTP (Curiously recurring template pattern):

template <class T>
struct BaseComponent
{
    T* as()
    {
        return static_cast<T*>(this);
    }

    virtual ~BaseComponent() {}
};

template <typename T>
struct Component : public BaseComponent<T>
{
    virtual ~Component() {}
};

struct PositionComponent : public Component<PositionComponent>
{
    float x, y, z;

    virtual ~PositionComponent() {}
};

This way you can do:

auto x = yourBaseComponent.as();

and have the right child type statically.

like image 166
Jiwan Avatar answered Apr 06 '23 06:04

Jiwan


The code that you present is correct and well formed, but the cast in general is not safe. If the actual object was not a PositionComponent, then the compiler would very gladly assume that it is and you would be causing undefined behavior.

If you replace the cast with dynamic_cast, then the compiler will generate code that at runtime verifies that the conversion is valid.

The real question is why would you need this. There are reasons, but more often than not the use of casts are an indication of issues with your design. Reconsider whether you can do better (i.e. redesign your code so that you don't need to go explicitly converting types)

like image 27
David Rodríguez - dribeas Avatar answered Apr 06 '23 05:04

David Rodríguez - dribeas