这样的垂头丧气吗?

时间:2013-08-12 20:10:18

标签: c++

假设我们有以下代码:

#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;
}

在T * as()方法中,我应该使用static_cast还是dynamic_cast?有时候转换会失败吗?我需要像这样使用dynamic_cast吗?

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

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

    return ptr;

4 个答案:

答案 0 :(得分:3)

在您的情况下,无法静态判断this是否是正确的类型。 您可能需要的是CRTP(奇怪的重复模板模式):

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() {}
};

这样你就可以:

auto x = yourBaseComponent.as();

并且拥有正确的子类型。

答案 1 :(得分:2)

您提供的代码是正确且格式良好的,但演员一般不安全。如果实际对象不是PositionComponent,那么编译器会很乐意假设它是,并且您将导致未定义的行为。

如果使用dynamic_cast替换转换,则编译器将生成在运行时验证转换有效的代码。

真正的问题是你为什么需要这个。有原因,但通常使用演员表示您的设计存在问题。重新考虑是否可以更好(即重新设计代码,以便您不需要明确转换类型)

答案 2 :(得分:1)

由于您使用的是unique_ptr<BaseComponent>,因此转换失败的情况很可能会自然发生:在向量中插入新数据并在不相关的位置进行数据的消耗,并以编译器的方式进行无法强制执行。

以下是无效演员的示例:

struct AnotherComponent : public Component<AnotherComponent>
{
    virtual ~AnotherComponent () {}
};

std::vector<std::unique_ptr<BaseComponent>> mComponents;
mComponents.emplace_back(new AnotherComponent);
// !!! This code compiles, but it is fundamentally broken !!!
auto *pos = mComponents[0]->as<PositionComponent>();
pos->x = 1337;

在这方面,使用dynamic_cast可以更好地防止as<T>功能的错误使用。请注意,不正确的用法可能不是故意的:只要编译器无法为您检查类型,并且您可能存在类型不匹配,您应该更喜欢dynamic_cast<T>

以下是small demo来说明dynamic_cast如何为您提供一定程度的保护。

答案 3 :(得分:1)

在投射从基类派生的多态对象时,应始终使用dynamic_cast

如果mComponents[0]不是PositionComponent(或从中派生的类),则上述代码将失败。由于让mComponents指向BaseComponent的指针的全部目的是为了让您可以将除PositionComponent个对象以外的其他内容放入向量中,我说您需要关注特殊情况。

一般来说,这是一种难闻的气味&#34;当您使用dynamic_cast时(或通常铸造从公共基类派生的对象)。通常,这意味着不应将对象保存在公共容器中,因为它们之间的关系不够密切。