【问题标题】:Is this use of reinterpret_cast on differently-qualified struct members safe?在不同的限定结构成员上使用 reinterpret_cast 是否安全?
【发布时间】:2016-01-23 13:51:32
【问题描述】:

我查看了以下相关的问题,但似乎都没有解决我的确切问题:onetwothree

我正在编写一个集合,其中存储了元素(键值对)以及一些簿记信息:

struct Element {
    Key key;
    Value value;
    int flags;
};

std::vector<Element> elements;

(为简单起见,假设KeyValue 都是标准布局类型。无论如何,该集合不会与任何其他类型一起使用。)

为了支持基于迭代器的访问,我编写了覆盖operator-&gt;operator* 的迭代器,分别向用户返回指向键值对的指针和引用。但是,由于集合的性质,永远不允许用户更改返回的密钥。为此,我声明了一个KeyValuePair 结构:

struct KeyValuePair {
    const Key key;
    Value value;
};

我已经在迭代器上实现了operator-&gt;,如下所示:

struct iterator {
    size_t index;

    KeyValuePair *operator->() {
        return reinterpret_cast<KeyValuePair *>(&elements[index]);
    }
};

我的问题是:reinterpret_cast 的这种使用是明确定义的,还是会调用未定义的行为?我试图解释标准的相关部分并检查了有关类似问题的问题的答案,但是,我未能从中得出明确的结论,因为……:

  • 这两种结构类型共享一些初始数据成员(即keyvalue),它们的区别仅在于const-qualification;
  • 标准没有明确说明Tcv T 是布局兼容的,但也没有说明相反的情况;此外,它还要求它们具有相同的代表性和一致性要求;
  • 如果第一个但许多成员具有布局兼容类型,则两个标准布局类类型共享一个共同的初始序列;
  • 对于包含共享公共初始序列的类类型成员的联合类型,允许使用任一联合成员​​ (9.2p18) 检查此类初始序列的成员。 – 没有类似的明确保证reinterpret_casted 指向结构的指针共享一个共同的初始序列。 – 但是,可以保证指向结构的指针指向其初始成员 (9.2p19)。

仅使用这些信息,我发现无法推断出 ElementKeyValuePair 结构是否共享一个共同的初始序列,或者有任何其他共同点可以证明我的 reinterpret_cast 是合理的。

顺便说一句,如果您认为将reinterpret_cast 用于此目的是不合适的,并且我确实面临 XY 问题,因此我应该简单地做些其他事情来实现我的目标,请告诉我。

【问题讨论】:

  • 您要求language-lawyer 判断吗?它真的有效吗? reinterpret_cast 几乎总是错误的方法,const_cast 不应该很好地满足您的要求吗?
  • @πάνταῥεῖ 是的,我正在请求语言律师的判断。 我无法判断它是否真的有效 或者它只是“似乎有效”,因为我不知道它是否会调用未定义的行为。我不知道const_cast 是否应该在这种情况下工作,但让我试一试——目前还不清楚const_cast 编译是否意味着这是明确定义的。
  • “但是,无论const_cast编译是否意味着这是定义明确的。” 当然,我认为这就是它的目的。使用reinterpret_cast 不能保证任何事情。

标签: c++ c++11 language-lawyer reinterpret-cast strict-aliasing


【解决方案1】:

我的问题是:这种对 reinterpret_cast 的使用是否定义明确? 它调用未定义的行为?

reinterpret_cast 在这里是错误的方法,您只是违反了严格的别名。 reinterpret_castunion 在这里的分歧有点令人困惑,但这种情况的措辞非常明确。

您最好这样简单地定义一个联合:

union elem_t {
   Element e{}; KeyValuePair p;
   /* special member functions defined if necessary */
};

... 并将其用作矢量元素类型。请注意,在确定布局兼容性时忽略 cv-qualification - [basic.types]/11:

两种类型 cv1 T1cv2 T2 是布局兼容类型,如果 T1T2 是同一类型,[…]

因此ElementKeyValuePair 确实共享一个共同的初始序列,并且如果e 是活动的,访问p 的相应成员是明确定义的。


另一种方法:定义

struct KeyValuePair {
    Key key;
    mutable Value value;
};

struct Element : KeyValuePair {
    int flags;
};

现在提供一个迭代器,它简单地将向量中的const_iterator 包装起来,并向上转换要公开的引用/指针。 key 不可修改,但 value 可以。

【讨论】:

  • 谢谢!听到reinterpret_cast 是完全错误的做法其实并不奇怪。
  • struct Element { struct KeyValuePair pair; int flags}没有错。
猜你喜欢
  • 2021-05-16
  • 2020-11-05
  • 2015-10-22
  • 1970-01-01
  • 2013-11-05
  • 2020-06-22
  • 2015-06-10
  • 2017-06-11
  • 1970-01-01
相关资源
最近更新 更多