【问题标题】:How to check a runtime type under void* pointer (simple types allowed, no base class)?如何检查 void* 指针下的运行时类型(允许简单类型,无基类)?
【发布时间】:2018-05-04 10:25:57
【问题描述】:

如何检查 void* 指针下的运行时类型。比如这样的函数怎么写:

void f(void *p) {
  // check if *p is an int or a vector
}

欢迎使用现代 C++ 版本(14、17)。作为未来的信息,未来的版本也很有趣。

没有基类,也没有通用的虚方法和简单类型,所以How to determine actual object type at runtime in C++; 不完全相关。

编辑:

好的,在某些情况下,如果调用者知道指针的真实类型,重载可能是上述简化示例的解决方案。但是稍微复杂一点的事情呢:

using ::std::vector;

void f(vector<void*> v) {
  // check if particular *v[i]'s are ints or vectors
}

函数声明中的 void* 也不是我的主意。

【问题讨论】:

  • 你为什么要这样做??
  • 这是你不应该做的事情。这个陈述甚至可以证明一个感叹号是合理的。除此之外,你不能这样做。
  • 在运行时无法查询无类型指针引用的内存中的内容。毕竟,内存只是一个原始的字节序列。每个字节都与任何其他字节相同。 C++ 不能以这种方式工作。
  • 如果您将void* 作为参数传递,并且您关心它是什么类型;那你就错了。
  • void *p 的语义是“p 可能指向也可能不指向某种数量的数据(或函数、套接字等)”。这在 C++ 中并不是那么有用。

标签: c++


【解决方案1】:

目前还不清楚,为什么你首先有一个void*。一旦你有了void*,任何关于实际类型的信息都会丢失,你想编写的函数在 C++ 中是不可能的。如果您想使用指向 int 的指针或指向 std::vector 的指针调用相同的方法,您宁愿使用重载:

void f(int* p) {
     std::cout << "f called with pointer to int";
}

template <typename T>
void f(std::vector<T>* p) {
     std::cout << "f called with pointer to vector";
}

【讨论】:

  • 谢谢!我最初的例子有点过于简单了。如果调用者知道指针的真实类型,那么重载可能是这种简单情况的解决方案。我更新了问题。
  • @km9c3uwc 将指针放在向量中并不会改变void* 不携带有关指针的实际类型的信息这一事实。使用void* 就像在说“我不想知道指针指向的对象的类型是什么,并且因为我想明确说明我正在使用void*。”在极少数情况下 void 指针很有用,但我所知道的要求您将有关实际类型的信息存储在某处
  • @km9c3uwc 指针没有什么神奇之处,它们存储一个地址,仅此而已。例如,即使int* 也不能保证指向int,但使用指针使其指向某个有效地址的人有责任。考虑int* x = 123;,我不知道有一种可移植的方式来知道*x 是否是有效的int
  • @km9c3uwc ...话虽如此,您应该找出为什么该函数应该将 void 指针作为参数,如果没有充分的理由,请说服编写该代码的人更改它
【解决方案2】:

这对于原始类型是不可能的,老实说,这是一个糟糕的主意。 void* 只是一个地址 - 这里没有任何信息可以用来推断指针的实际类型是什么。

实际上,可以为多态类型执行此操作的原因之一是因为运行时类型信息可以存储在 vtable 中。 (请注意,标准不要求使用 vtable,但它是最常见的实现。)

【讨论】:

  • 这也不能用于多态类型。当然,您可以猜测您的编译器将 vtable 指针放置在距对象地址的特定偏移量处,但是语言定义不需要使用 vtable,也没有说明如果以这种方式实现多态性,则 vtable 指针的位置.
  • @PeteBecker 我认为 Vittorio 提到了 OP 链接的问题,它有一个 Base* 而不是 void*。这为语言开始理解那里存储的内容(即某种类型的 vtable 指针)提供了一个起点。 void* 的意义为零,所以什么也做不了。
【解决方案3】:

忘记void*。这是一张单程票;一旦你介入,就没有回头路了,所有静态类型信息都一去不复返了。 如果使用的类型集未绑定,请选择std::any(c++17 或 boost)。它可能会触发动态分配,但也有小的价值优化: http://en.cppreference.com/w/cpp/utility/any 但如果使用的类型集是可数且有界的,请考虑 std::variant(c++14 或 boost)。您可以调查静态访问者模式: http://en.cppreference.com/w/cpp/utility/variant

【讨论】:

    猜你喜欢
    • 2012-10-05
    • 1970-01-01
    • 1970-01-01
    • 2021-12-31
    • 2012-08-15
    • 1970-01-01
    • 2015-02-14
    • 2015-10-09
    相关资源
    最近更新 更多