继承类型的指针之间的静态强制转换

static cast between pointers of inherited types

本文关键字:静态 转换 之间 类型 指针 继承      更新时间:2023-10-16

我正在尝试进行

static_cast<IntrusivePtr<B>>(IntrusivePtr<A>)

其中B公开来源于A。然而,这给出了错误

invalid conversion from A* to B*

有人能向我解释一下这里的问题吗?我可以通过做来绕过它

static_cast<B*>(IntrusivePtr<A>.get())

从理论上讲,这正是上述错误信息所说的不允许的。这是GCC 4.4.7

您要查找的概念称为类型协方差-您希望IntrusivePtr<T>T上是协变的,这意味着如果DB的子类型,则IntrusivePtr<D>IntrusivePtr<B>的子类型。有些语言的类型系统支持这一点,C++不是其中之一。

在C++中,无论AB是什么,IntrusivePtr<A>IntrusivePtr<B>都是完全不相关的类型(除非它们是同一类型)。如果你想支持这种转换,你必须明确地支持它:

template <class T>
struct IntrusivePtr {
    template <class U, class = std::enable_if_t<std::is_convertible<U*, T*>::value>>
    IntrusivePtr(IntrusivePtr<U> const& rhs) { ... }
};

或者执行您正在执行的操作,并绕过IntrusivePtr类模板。


回顾一下这个问题,IntrusivePtr似乎更可能支持上述转换,但在这两种类型之间进行了隐式转换——这对派生到基有效,但对派生到基无效。但您始终可以显式地从基强制转换为派生。即:

struct B { };
struct D : B { };
B b;
D* d1 = &b;                  // error
D* d2 = static_cast<D*>(&b); // ok

IntrusivePtr<A>IntrusivePtr<B>是完全不相关的类型。因此,不能在它们之间静态转换(除非IntrusivePtr包含显式转换代码,或者AB相同)。

注意,不能从基类静态地转换为派生类。

以下是如何更改IntrusivePtr的一种方法。

template <typename T>
struct IntrusivePtr {
    IntrusivePtr(const IntrusivePtr& other) {
         ... // do the refcounting
         ptr = other.ptr; // straight assignment, pointers are of the same type
    }
    template <typename Q,
       typename = std::enable_if_t<std::is_convertible<Q*, T*>::value>>
    IntrusivePtr(const IntrusivePtr<Q>& other) {
        ... // do the refcounting
        ptr = other.ptr; // straight assignment, pointers are compatible
    }
    // this can only be used in explicit conversions
    template <typename Q,
       typename = std::enable_if_t<std::is_convertible<T*, Q*>::value>>
    // must add bogus default parameter, or the compiler will complain
    explicit IntrusivePtr(const IntrusivePtr<Q>& other, int=0) {
        ... // do the refcounting
        ptr = static_cast<T*>(other.ptr); // explicit downcast, trust the programmer
    }
 ... // other methods
 T* ptr;
};

检查:

struct A {};
struct B : A {};
IntrusivePtr<A> a;
IntrusivePtr<B> b;
a = a; // ok, straight copy
a = b; // ok, implicit upcast
b = static_cast<IntrusivePtr<B>>(a); // ok, explicit downcast
b = a; // error, implicit conversion is not allowed

但是,如果IntrusivePtr离boost::intrusive_ptr不远,它应该具有类似于boost::intrusive_ptr::static_pointer_cast的功能。可能应该使用它,尤其是对于不支持C++11的编译器。