我们能否在运行时确定两个 type_info 是否可转换?

Wor*_*der 5 c++ dynamic-cast typeid typeinfo

有没有办法从两个const ::std::type_info对象中确定,让我们命名它们,B以及DD 描述的类型是否从类型 B 派生?

我问是因为我想删除我得到的对象的类型,但稍后能够检查它是否可以安全地提升。

void* data;
const ::std::type_info* D;

template<typename D>
void store(D&& object)
{
    D = &typeid(object);
    data = ::std::addressof(object);
}

template<typename B>
B& load()
{
    // if(typeid(B) != (*D)) throw ::std::bad_cast{};
    return *reinterpret_cast<B*>(data); // <- also problematic
}
Run Code Online (Sandbox Code Playgroud)

我希望能够像这样使用它:

class Base {};
class Derived : Base {};

Derived d;
store(d);
// ....
load<Base>();
Run Code Online (Sandbox Code Playgroud)

因此,只对 typeid 使用相等比较是不合适的。我很确定这可能以类似于 dynamic_cast 可以解决这个问题的方式成为可能。我想要的是,在每种情况下D&都可以指定B&允许 B 作为 - 的类型参数load()-D当时不知道。

Wor*_*der 3

我找到了一种方法让编译器和内部机制为我解决这个问题。我对交叉编译没有问题,在这种情况下::std::type_info也不一致。

typedef void (*throw_op)(void*);
throw_op dataThrow;

template<typename T>
[[ noreturn ]] void throwing(void* data)
{
    throw static_cast<T*>(data);
}
[[ noreturn ]] void bad_cast()
{
    throw ::std::bad_cast{};
}

template<typename B>
B& poly_load()
{
    if(data == nullptr)
        bad_cast();
    try {
        dataThrow(data);
    } catch (B* ptr) {
        return *ptr;
    } catch (...) {
        bad_cast();
    }
}
Run Code Online (Sandbox Code Playgroud)

您所要做的就是将以下行添加到存储操作中:

dataThrow = throwing<D>;
Run Code Online (Sandbox Code Playgroud)

这是如何运作的?它利用了异常及其捕获方式。请注意,这使得poly_load“很多”?比简单load函数慢,因此我将保留两者。

D*C++ 表示,当抛出类型异常时,您可以使用 catch 子句捕获该异常B*,其中B是 的任何祖先D

最小的例子:

struct Base {
    virtual ~Base() {}
    virtual void foo() = 0;
};

struct Derived : public virtual Base {
    void foo() override {
        ::std::cout << "Hello from Derived" << ::std::endl;
    }
};

int main() {
    Derived d{};
    store(d);

    // .....

    poly_load<Base>().foo();
}
Run Code Online (Sandbox Code Playgroud)