引用类型擦除的 void*
Posted
技术标签:
【中文标题】引用类型擦除的 void*【英文标题】:Reference to type-erased void* 【发布时间】:2019-08-30 23:51:44 【问题描述】:我正在实现一个迭代器适配器,它允许处理保存void*
的旧数据类型,我想获得一个forward_iterator
,它允许交换旧数据类型的值,方法是给用户一个保存在该结构中的真实指针的视图。示例:
auto it = iterator_adaptor<T*>(pos);
其中pos->object
是一个void*
,它最初的类型是T*
。事情是关于引用类型:
// within iterator_adaptor
typedef T*& reference;
// I want to promise that to the user.
typedef std::forward_iterator_tag iterator_category;
reference operator*() const return static_cast<reference>(_pos->object);
这会产生编译器错误,因为我无法将引用包装在不同类型的对象上。如果类型相关,我可以在引用之间或指针之间进行转换,但是知道_pos->object
指向@ 类型的对象,我怎么能以非未定义的行为方式将void*
左值转换为T*&
987654331@?
我能想到的唯一可能被编译器吞下的是:
return *reinterpret_cast<T**>(&_pos->object);
或者这个方向的东西,但必须以 100% 的概率将其定义为标准未定义的行为。
注意:我想返回 T*&
,而不是 T&
,因为每个 T
的某些语义是由其地址定义的(具体来说,有映射 @ 987654336@ 到它的地址,因为每个T::id()
对于T*
都是唯一的)。如果我返回T&
并且用户交换它们,地址和id
不再匹配,举一些可能破坏应用程序的例子。我宁愿允许用户在结构中交换每个T*
的位置(因为用户毕竟保存了指针;每个T
都是在插入结构之前动态创建的),例如个性化其排序,或者使用任何需要转发和输入迭代器的std
算法。
实际上,“交换”位置并不是那么重要,但是我想提供一个功能,将<algorithm>
库用于需要前向迭代器的算法。
【问题讨论】:
我要去另一个方向。我正在重新审视我的旧 C 代码接口,以使它们尽可能地键入。不让void*
活着....
你可能会返回一个包装器而不是一个真正的引用:typedef my_ref_wrapper<T*> reference;
@TedLyngmo 我实际上正在按照你说的做。我正在尝试再次输入所有内容。
@Jarod42 这不会产生 ForwardIterator。
typedef struct void* ptr_ A;
工作......并且即使在 C 中也进行了类型检查。@Peregring-lk 太好了!这对我很有帮助。
【参考方案1】:
好的,让我直说吧(mcve 会很有帮助):
你有这种情况:
X x1, x2;
X* p = &x1;
void* vp = reinterpret_cast<void*>(p);
// p is lost
// here you want to recover p such that:
X*& q = /* something magic from vp */;
q = &x2; // this will modify p
如果这是不可能的情况,因为您永远丢失了对象p
。您在vp
中保存了p
所指的内容,也就是您保存了它的值,也就是您保存了x1
的地址(以类型擦除的方式),这是可恢复的,指针是可恢复的(如果您知道原始type),但 p
丢失了,它从未保存过。
如果你想恢复p
,那么你需要保存它的地址:
X x111, x227;
X* p = &x1;
void* vpp = reinterpret_cast<void*>(&p);
// p must not end lifetime !! very important
X*& q = *reinterpret_cast<X**>(vpp);
q = &x2; // will indeed modify p (p must still be alive)
否则你可以这样做,它完全有效:
T& a = *reinterpret_cast<T*>(pos->object);
T* p = reinterpret_cast<T*>(pos->object);
最后是一些标准甜点(重点是我的):
§8.5.1.10 重新解释演员表 [expr.reinterpret.cast]
对象指针可以显式转换为不同类型的对象指针。 73 当对象指针的prvalue v type 转换为对象指针类型“pointer to cv T”, 结果是
static_cast<cv T*>(static_cast<cv void*>(v))
。 [ 笔记: 将“指向 T1 的指针”类型的纯右值转换为“指向 T1 的指针”类型 T2”(其中 T1 和 T2 是对象类型,其中对齐 T2 的要求并不比 T1 的要求更严格)然后回到它的 原始类型产生原始指针值。 ——尾注]
最简单的例子:
X* p = /* ... */;
void* v = reinterpret_cast<void*>(p);
X* q = reinterpret_cast<X*>(v);
// q is guaranteed to have the original value of p,
// i.e. p == q is true
【讨论】:
以上是关于引用类型擦除的 void*的主要内容,如果未能解决你的问题,请参考以下文章
Java泛型元组类型参数推断擦除擦除的补偿通配符等的学习及运行
Java泛型元组类型参数推断擦除擦除的补偿通配符等的学习及运行
Java泛型元组类型参数推断擦除擦除的补偿通配符等的学习及运行