从 "void *" 进行 dynamic_cast

47
根据这个问题,void*没有RTTI信息,因此从void*强制转换是不合法的且不合理的。
如果我没记错的话,在gcc上从void*进行dynamic_cast是可以的。
请你澄清这个问题。
6个回答

51

dynamic_cast 仅适用于多态类型,即包含虚函数的类。

在GCC中,您可以将dynamic_cast用于void*,但不能从其转换

struct S
{
    virtual ~S() {}
};

int main()
{
    S* p = new S();
    void* v = dynamic_cast<void*>(p);
    S* p1 = dynamic_cast<S*>(v); // gives an error
}

24
在《5.2.7 - dynamic_cast [expr.dynamic.cast]》中,对于dynamic_cast<T>(v)说道:
  • 如果T是指针类型,则v应该是指向完整类类型的rvalue
  • 如果T是引用类型,则v应该是一个完整类类型的lvalue(感谢usta评论我的遗漏)

...

  • 否则,v应该是一个多态类型的指针或lvalue

所以,不,不允许使用(void*)

思考一下你的要求可能意味着什么:假设你有一个指针,真正指向一个Derived1*,但是代码dynamic_cast只知道它是一个void*。假设您试图将其转换为指向Derived2*的指针,其中两个派生类具有共同的基类。表面上看,您可能认为所有指针都将指向相同的Base对象,其中包含对相关虚拟调度表和RTTI的指针,因此一切都可以相互关联。但是,请注意,派生类可能有多个基类,因此所需的Base类子对象可能不是指向Derived*所指向的对象。只能作为void* - 这不起作用。结论:编译器需要知道这些类型,以便根据涉及的类型对指针进行一些调整。

Derived1* -----> [AnotherBase]
                 [[VDT]Base]    <-- 但是,需要一个指向开始的指针
                 [extra members]    这个子对象用于dynamic_cast。

(有些答案谈到需要转换的指针是多态类型,具有虚函数。那都是有效的,但是有点误导人。就像上面看到的一样,即使void*也指向这样的类型,如果没有完整的类型信息,仍然不能可靠地工作,因为真正的问题是void*可能指向派生对象的开头,而您需要一个指针指向从中派生出转换的类型的基类子对象。)


如果T是指针类型,则v应该是指向完整类类型的指针的rvalue,... 如果T是引用类型,则v应该是完整类类型的lvalue,... - usta

4

确实,void* 无法进行 dynamically_cast 转换。

你可能记错了。使用 g++ 4.5 和以下代码:

struct A {
    virtual ~A();
};

int main() {
    A a;
    void *p = &a;
    A* pa = dynamic_cast<A*>(p);
}

我收到了以下错误信息:

无法将类型为“void *”的 'p' 动态转换为类型为“struct A*” 的内容(源不是指向类的指针)


该错误与动态类型转换有关,可能是由于将指针转换为未知类型而导致的。建议检查代码中的类型声明和指针使用情况。

3

除了Tony的不错的回答之外,这段代码片段出乎意料地帮助了我。首先,我们建立了一个简单的层次结构。然后,我们看看 dynamic_cast 是否能够“承受”一个 static_cast。在这个实验之前,我认为“运行时类型信息是存在的,dynamic_cast 应该能够找到它。”现在我意识到“dynamic_cast 必须要根据编译器知道的一些表格来查找信息,所以它不能有某些神奇的能力。”

#include <iostream>
#include <cassert>

using namespace std;

class A {
  protected:
  virtual void foo() { cout << "A" << endl; }
};

class B1 : public A {
  private:
  virtual void foo() override { cout << "B1" << endl; }
};

class B2 : public A {
  public:
  virtual void foo() override { cout << "B2" << endl; }
};

int main(int argc, char **argv) {
  B1 b1;
  // undefined behavior even though dynamic_cast didn't return null
  dynamic_cast<B2*>(
      static_cast<B2*>(
        static_cast<A*>(&b1)))->foo();
  // dynamic_cast returns null here though
  assert (!dynamic_cast<B2*>
          (static_cast<A*>
           (static_cast<B2*>
            (static_cast<A*>(&b1)))));
}

2
我猜你可能会把dynamic_cast弄混了,它可以将指针转换为void*类型。这是合法的,并且可以获得指向最终派生类对象的指针。
void*进行dynamic_cast是不合法的 - 要转换的类型必须是多态的 - 至少包含一个虚函数(虚析构函数也算)。

除非类型转换的类是被转换表达式的类类型的一个明确可访问的基类,否则后者不需要是多态的。 - usta

0

你可以将指向多态类型的指针转换为void *,但反之则不行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接