【问题标题】:Reference to type-erased void*引用类型擦除的 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 太好了!这对我很有帮助。

标签: c++ casting reference lvalue


【解决方案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 x1{11}, x2{27};
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]

  1. 对象指针可以显式转换为不同类型的对象指针。 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

【讨论】:

    猜你喜欢
    • 2020-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-28
    • 1970-01-01
    相关资源
    最近更新 更多