我正在实现一个特殊用途的句柄类。
ihandle是所有句柄都必须实现的接口(interface),在我的真实代码中,它将为-> *提供操作符重载。但是对于这个例子,我想保持简单,它只有get函数。

template <typename T>
class ihandle {
public:
    virtual T* get();
};

一种可能的实现方式是ptr,它只是一个原始指针。
template <typename T>
class ptr : public ihandle<T>
{
    T* t;
public:
    ptr(T* t = nullptr) : t(t) {}
    T* get(){return t;}
};

然后是handle,旨在进行空安全检查。
template <typename T>
class handle
{
public:
    ihandle<T>* h;

    T* get(){return h->get();}
    handle(ihandle<T>* h = nullptr) : h(h) {}

    template <typename D>
    handle(handle<D>& hd)
        : h((ihandle<T>*)hd.h)
    {
        static_assert(is_base_of<T, D>::value, "error");
    }
};

这个构造函数可以将继承类的句柄转换为基类的句柄。
template <typename D>
handle(handle<D>& hd)
    : h((ihandle<T>*)hd.h)
{
    static_assert(is_base_of<T, D>::value, "error");
}

例如,如果B继承自A,那么我希望能够使用handle<B>实例调用此函数。
void foo(handle<A> ha) {
    // do something
}

但这为下面的示例测试提供了错误。
struct A {
    virtual void talk() {printf("A\n");}
};

struct B : public A {
    void talk() {printf("B\n");}
};

int main()
{
    handle<B> hb(new ptr<B>(new B));
    //hb.get()->talk(); // if uncomment, no segfault
    handle<A> ha = hb;
    ha.get()->talk(); // segfault here

    return 0;
}

我怀疑问题可能出在handle(handle<D>& hd)构造函数中,但我不知道发生了什么。

您可以通过单击以下链接进行测试:
https://onlinegdb.com/BkAYuQZ3z

最佳答案

即使条件is_base_of<T, D>::value为true,也不会使(ihandle<T>*) hd.h转换有效,因为ptr<B>ihandle<A>类型不相关。我想这是应该绝对避免使用c样式强制转换的示例。为了安全地执行转换,您可以通过检查使用dynamic_cast:

: h(dynamic_cast<ihandle<T>*>(hd.h))
{
   if(hd.h && !h)
   {
       throw ::std::runtime_error{"pointers are not related"};
   }
}

08-16 21:53