在void*中存储指向T的指针,但T可能是const或nonconst - 使用void const*还是只使用void*?

3
当模板代码需要在变量类型为 void * 的变量中存储 T * ,以便稍后可以检索并强制转换回 T *,但它需要处理 T T const * 时,应该使用 void * 还是 void const * 进行类型擦除存储?(任何一种选择都需要至少一个const转换。)
(假设程序在运行时跟踪将void*放入其中以确保仅检索与放入的相同类型,但它在编译时不知道将要存储在变量中的类型。)
编辑:还要假定未键入的存储隐藏在具有模板set()和get()方法的包装类后面,这些方法记录用于set()调用的T,如果用于get()调用不同的T,则抛出异常。所以问题实际上是关于包装类如何存储它。 (注意:包装类本身不是一个模板;只是它的方法set()和get()是模板。)
过去,当遇到这个问题时,我使用了union {void * p_nc; void const * p_c;};但我想知道这是否比仅使用const_cast过度。
问题是,哪种方式更好(如果有):将 T const * 的const转换为 void * 中的存储,还是在检索非const T *时使用 void * const 进行存储并将const转换关闭?

有没有什么理由不直接存储这两个指针,当使用const指针时将非const指针设置为nullptr?对我来说,这似乎是显而易见的做法。 - David Schwartz
大卫 - 我也考虑过这个问题,几乎要把它加入到问题中。然后我意识到它在功能上等同于“联合”情况,只是存储方式不同。 - Dennis
这并不完全是因为它不需要单独检查哪个指针有效。(当您设置一个非const指针时,您会同时设置两个指针。) - David Schwartz
对,但问题是代码必须在 get() 函数中知道要转换成什么类型,因此它已经知道它是否为 const。这将是一个单独的变量,用于存储指针中的类型。它可以是从 RTTI type_id 派生出来的东西,也可以是枚举值等。 - Dennis
除了“const”之外,存储为“void”,实际类型为“T”。当指针被存储时,“T”类型丢失。当检索存储的值时,它具有“void”类型,并且必须转换回“T”类型。例如,假设我们正在讨论存储“int”,那么“T”可以很容易地变为“int const”或“int”。因此,如果我们在检索时知道“T”,我们也知道它是“int”还是“int const”。 - Dennis
1个回答

2
从读者的角度来看(包括你未来六个月后的自己),最好尽可能清晰地表达思想,而不是试图将强制转换的次数降至最少。
在您的情况下,使用union表示“它可以是任何一个”,但它并没有对实际执行进行任何检查。
另一方面,具有明确界面的实现两个const和非const访问函数(包括转换函数)允许您执行运行时检查并相应地采取行动(错误消息?异常?)。
一个清晰的接口具有封装实现的附加好处,以便您的客户端代码不依赖于实现的内部结构 - 您的客户端代码将只使用您的模板作为const或非const(根据用途),并且访问/转换函数将负责确保实际使用是有效的。

谢谢,但我更假设隐藏存储的接口存在,并询问该接口的实现应如何处理存储指针。 我省略了描述如何确保正确类型访问的方法,因为我不想让问题太长。可能是add()为每个T分配唯一的ID,也可能是类型数量很少以使用枚举变量。 - Dennis

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