不能动态强制转换为ptr

Cannot dynamic cast to ptr

本文关键字:转换 ptr 不能动 动态 不能      更新时间:2023-10-16

我试图实现非常基本的类类似于boost::any,但有一个问题,我正面临着,我不能越过这个。问题是使用dynamic_cast将指针转换为另一个指针,错误说:

cannot dynamic_cast 'input.Any::m_targetPtr' (of type 'class Any::StorageInterface*') to type 'int*' (target is not pointer or reference to class)

类的来源如下:

class Any{
public:
    template <typename T>
    Any(T input): m_targetPtr(new StorageImpl<T>(input)){}
    ~Any(){delete m_targetPtr;}
    class StorageInterface{
    public:
        virtual ~StorageInterface(){}
    };
    template<typename T>
    T* cast(Any& input){
        return dynamic_cast<T*>(input.m_targetPtr); //here comes the trouble
    }
    template <typename T>
    class StorageImpl : public StorageInterface{
    public:
        StorageImpl(T& input): m_target(&input){}
        T* m_target;
    };
    StorageInterface* m_targetPtr;
};

我想这样执行:

int i=150;
Any asdf(i);
cout<< (asdf.cast<int>(asdf)) << endl;

我的理解是。我有一个int变量和cast模板,我传递指针到这个int和模板中的T是int,因此在转换源中,我们有int作为推导参数int*作为返回值,我用StorageInterface*类型的参数m_target执行dynamic_cast<int*>。为什么我得到一个错误,我的目标不是一个指针?

我在以下代码中看到以下问题:

template<typename T>
T* cast(Any input){
    return dynamic_cast<T*>(input.m_targetPtr); //here comes the trouble
}
  1. 您需要强制转换为StorageImpl<T>*,而不是T*

  2. StorageImpl不公开m_target成员

公开m_target成员,将其设置为public或提供返回值的函数。

修改cast函数为:

template<typename T>
T* cast(Any input){
    return dynamic_cast<StorageImpl<T>*>(input.m_targetPtr)->m_target;
}

改进建议:

cast中不需要input参数。它可以是:

template<typename T>
T* cast(){
    return dynamic_cast<StorageImpl<T>*>(m_targetPtr)->m_target;
}

然后,您的调用也可以简化为:

cout<< (asdf.cast<int>()) << endl;
                  //  ^^^ No need to use asdf again in the call.

进一步改进建议

代码中的StorageImpl存储了一个指向对象的指针,这个指针很容易失效。我建议存储一个对象。这样,Any::cast()就可以返回一个引用,而不是一个指针。

class Any {
   public:
      template <typename T>
         Any(T input): m_targetPtr(new StorageImpl<T>(input)){}
      ~Any(){delete m_targetPtr;}
      class StorageInterface {
         public:
            virtual ~StorageInterface(){}
      };
      template<typename T>
         T const& cast() const {
            return dynamic_cast<StorageImpl<T>*>(m_targetPtr)->m_target;
         }
      template<typename T>
         T& cast() {
            return dynamic_cast<StorageImpl<T>*>(m_targetPtr)->m_target;
         }
      template <typename T>
         class StorageImpl : public StorageInterface {
            public:
               StorageImpl(T const& input): m_target(input){}
               T m_target;
         };
      StorageInterface* m_targetPtr;
};

问题是,如果X不属于与T相同的类层次结构,dynamic_cast<T>(X)无法将类型X转换为类型T。动态强制转换意味着多态性,例如,将Animal*向下强制转换为Dog*,其中class Dog继承自class Animal, class Animal包含虚函数。问题是int不是类类型,所以dynamic_cast可以很早就得出intStorageInterface不共享类层次结构的结论。

错误信息不是说你的目标不是指针,而是说你的目标不是指向类对象的指针(也不是对类对象的引用)。

R Sahu指出,您打算转换为StorageImpl<T>*,这更有意义。

dynamic_cast<T>用于将一个多态结构体的指针转换为同一层次结构中的另一个多态结构体。多态结构是具有至少一个虚函数或虚继承的结构。显然,int*不是结构体,也不是多态的。

你可能想用这个来代替:

template<typename T>
T* cast(Any input){
    return dynamic_cast<StorageImpl<T>*>(input.m_targetPtr)->m_target; // better?
}

为什么要发送对象的副本给自身呢?如果将函数限制为实例本身,则更不容易出错:

template<typename T>    
T* cast(){
    return dynamic_cast<StorageImpl<T>*>(m_targetPtr)->m_target; // even better
}

你现在可以这样使用:

asdf.cast<int>();

最后,为了使您的代码更干净,更不容易出错,可以做最后一件事。你看到潜在的未定义行为了吗?您的代码中有双重删除:

template<typename T>
T* cast(Any input){
    return dynamic_cast<T*>(input.m_targetPtr); //here comes the trouble
    // input is deleted here, so asdf will have a dangling pointer and double delete the m_targetPtr!
}
在你的构造函数中,你保存了一个临时的地址:
// input is a reference to a variable from the 'Any' class constructor, it will be deleted
StorageImpl(T& input): m_target(&input){}

通过value(和move)传递可以解决这个问题。

任何使用这个类的代码都可能出现这些错误。使用std::unique_ptr和值语义可以达到这个目的:

struct Any {
    template <typename T>
    Any(T input): m_targetPtr(new StorageImpl<T>(std::move(input))){}
    /* ~Any(){delete m_targetPtr;} */
    // no destructor needed here, you can safely remove the line above.
    // you could implement a copy constructor which clone the m_targetPtr.
    class StorageInterface{
    public:
        virtual ~StorageInterface(){}
    };
    template<typename T>
    T& cast(){
        return dynamic_cast<StorageImpl<T>*>(m_targetPtr.get())->m_target;
    }

    template <typename T>
    class StorageImpl : public StorageInterface{
    public:
        StorageImpl(T input): m_target(std::move(input)){}
        T m_target;
    };
private:
    std::unique_ptr<StorageInterface> m_targetPtr;
};