在不同限定的struct成员上使用reinterpret_cast是否安全?

时间:2015-10-23 22:25:37

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

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

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

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

std::vector<Element> elements;

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

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

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

我在迭代器上实现了operator->,如下所示:

struct iterator {
    size_t index;

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

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

  • 两个结构类型共享一些初始数据成员(即keyvalue),这些成员仅在const - 资格认证方面有所不同;
  • 标准没有明确说明Tcv T是布局兼容的,但它也没有说明相反的情况;此外,它要求它们具有相同的表示和对齐要求;
  • 如果第一个有多个成员具有布局兼容类型,则两个标准布局类类型共享一个共同的初始序列;
  • 对于包含共享一个公共初始序列的类类型成员的联合类型,允许使用任一联合成员​​(9.2p18)检查此类初始序列的成员。 - 对于共享一个公共初始序列的reinterpret_cast ed指针到结构,没有类似的明确保证。 - 但是,保证指向struct的指针指向其初始成员(9.2p19)。

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

顺便说一句,如果您认为使用reinterpret_cast用于此目的是不合适的,而且我真的面临XY问题,因此我应该做其他事情来实现我的目标,让我知道。

1 个答案:

答案 0 :(得分:7)

  

我的问题是:这是使用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 T1 cv2 T2是布局兼容类型,如果   T1T2属于同一类型,[...]

因此,ElementKeyValuePair确实共享一个共同的初始序列,并且访问p的相应成员,如果e存在,则定义良好。< / p>

另一种方法:定义

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

struct Element : KeyValuePair {
    int flags;
};

现在提供一个迭代器,它只是从向量中包装const_iterator并向上转换要公开的引用/指针。 key无法修改,但value将是。