引用类型擦除的 void*

Posted

技术标签:

【中文标题】引用类型擦除的 void*【英文标题】:Reference to type-erased void* 【发布时间】:2019-08-30 23:51:44 【问题描述】:

我正在实现一个迭代器适配器,它允许处理保存void* 的旧数据类型,我想获得一个forward_iterator,它允许交换旧数据类型的值,方法是给用户一个保存在该结构中的真实指针的视图。示例:

auto it = iterator_adaptor<T*>(pos);

其中pos-&gt;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-&gt;object 指向@ 类型的对象,我怎么能以非未定义的行为方式将void* 左值转换为T*&amp; 987654331@?

我能想到的唯一可能被编译器吞下的是:

return *reinterpret_cast<T**>(&_pos->object);

或者这个方向的东西,但必须以 100% 的概率将其定义为标准未定义的行为。

注意:我想返回 T*&amp;,而不是 T&amp;,因为每个 T 的某些语义是由其地址定义的(具体来说,有映射 @ 987654336@ 到它的地址,因为每个T::id() 对于T* 都是唯一的)。如果我返回T&amp; 并且用户交换它们,地址和id 不再匹配,举一些可能破坏应用程序的例子。我宁愿允许用户在结构中交换每个T* 的位置(因为用户毕竟保存了指针;每个T 都是在插入结构之前动态创建的),例如个性化其排序,或者使用任何需要转发和输入迭代器的std 算法。

实际上,“交换”位置并不是那么重要,但是我想提供一个功能,将&lt;algorithm&gt; 库用于需要前向迭代器的算法。

【问题讨论】:

我要去另一个方向。我正在重新审视我的旧 C 代码接口,以使它们尽可能地键入。不让void*活着.... 你可能会返回一个包装器而不是一个真正的引用:typedef my_ref_wrapper&lt;T*&gt; 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&lt;cv T*&gt;(static_cast&lt;cv void*&gt;(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*的主要内容,如果未能解决你的问题,请参考以下文章

c_cpp 类型擦除的C ++范围

Java泛型元组类型参数推断擦除擦除的补偿通配符等的学习及运行

Java泛型元组类型参数推断擦除擦除的补偿通配符等的学习及运行

Java泛型元组类型参数推断擦除擦除的补偿通配符等的学习及运行

我们可以在 Swift 中创建具有非可选属性的类型擦除弱引用吗?

Java泛型:类型擦除