在不同限定的结构成员上使用 reinterpret_cast 安全吗?
Is this use of reinterpret_cast on differently-qualified struct members safe?
我查看了以下相关的问题,其中 none 似乎解决了我的确切问题:one, two, .
我正在编写一个集合,其中的元素(键值对)与一些簿记信息一起存储:
struct Element {
Key key;
Value value;
int flags;
};
std::vector<Element> elements;
(为简单起见,假设 Key
和 Value
都是标准布局类型。该集合无论如何都不会与任何其他类型一起使用。)
为了支持基于迭代器的访问,我编写了覆盖 operator->
和 operator*
到 return 的迭代器,分别向用户提供指针和引用键值对。但是,由于集合的性质,永远不允许用户更改 returned 密钥。为此,我声明了一个 KeyValuePair
结构:
struct KeyValuePair {
const Key key;
Value value;
};
我已经在迭代器上实现了 operator->
,如下所示:
struct iterator {
size_t index;
KeyValuePair *operator->() {
return reinterpret_cast<KeyValuePair *>(&elements[index]);
}
};
我的问题是:reinterpret_cast
的这种用法是定义明确的,还是调用了未定义的行为?我试图解释标准的相关部分并检查了有关类似问题的问题的答案,但是,我未能从中得出明确的结论,因为……:
- 这两种结构类型共享一些初始数据成员(即
key
和 value
),仅在 const
限定上有所不同;
- 标准没有明确说明
T
和 cv T
是布局兼容的,但也没有相反的说明;此外,它还规定它们应该具有相同的表示和对齐要求;
- 两个标准布局 class 类型共享一个共同的初始序列,如果第一个但许多成员具有布局兼容的类型;
- 对于包含共享公共初始序列的 class 类型成员的联合类型,允许使用任一联合成员 (9.2p18) 检查此类初始序列的成员。
– 对于
reinterpret_cast
ed 指向结构的指针共享一个公共初始序列,没有类似的明确保证。
– 但是,可以保证指向结构的指针指向其初始成员 (9.2p19)。
仅使用这些信息,我发现无法推断 Element
和 KeyValuePair
结构是否共享一个共同的初始序列,或者是否有任何其他共同点可以证明我的 reinterpret_cast
.
顺便说一句,如果您认为为此目的使用 reinterpret_cast
是不合适的,而且我确实面临 XY 问题,因此我应该简单地做一些其他事情来实现我的目标,请告诉我。
My question is: is this use of reinterpret_cast well-defined, or does
it invoke undefined behavior?
reinterpret_cast
在这里是错误的方法,你只是违反了严格的别名。 reinterpret_cast
和union
在这里出现分歧有点令人费解,但是这种情况的措辞非常清楚。
这样简单地定义联合可能会更好:
union elem_t {
Element e{}; KeyValuePair p;
/* special member functions defined if necessary */
};
... 并将其用作矢量元素类型。请注意,在确定布局兼容性时忽略 cv 限定 - [basic.types]/11:
Two types cv1 T1
and cv2 T2
are layout-compatible types if
T1
and T2
are the same type, […]
因此 Element
和 KeyValuePair
确实共享一个共同的初始序列,并且访问 p
的相应成员,前提是 e
是活动的,是明确定义的.
另一种方法:定义
struct KeyValuePair {
Key key;
mutable Value value;
};
struct Element : KeyValuePair {
int flags;
};
现在提供一个迭代器,它简单地包装向量中的 const_iterator
并向上转换要公开的 references/pointers。 key
不可修改,但 value
可以。
我查看了以下相关的问题,其中 none 似乎解决了我的确切问题:one, two,
我正在编写一个集合,其中的元素(键值对)与一些簿记信息一起存储:
struct Element {
Key key;
Value value;
int flags;
};
std::vector<Element> elements;
(为简单起见,假设 Key
和 Value
都是标准布局类型。该集合无论如何都不会与任何其他类型一起使用。)
为了支持基于迭代器的访问,我编写了覆盖 operator->
和 operator*
到 return 的迭代器,分别向用户提供指针和引用键值对。但是,由于集合的性质,永远不允许用户更改 returned 密钥。为此,我声明了一个 KeyValuePair
结构:
struct KeyValuePair {
const Key key;
Value value;
};
我已经在迭代器上实现了 operator->
,如下所示:
struct iterator {
size_t index;
KeyValuePair *operator->() {
return reinterpret_cast<KeyValuePair *>(&elements[index]);
}
};
我的问题是:reinterpret_cast
的这种用法是定义明确的,还是调用了未定义的行为?我试图解释标准的相关部分并检查了有关类似问题的问题的答案,但是,我未能从中得出明确的结论,因为……:
- 这两种结构类型共享一些初始数据成员(即
key
和value
),仅在const
限定上有所不同; - 标准没有明确说明
T
和cv T
是布局兼容的,但也没有相反的说明;此外,它还规定它们应该具有相同的表示和对齐要求; - 两个标准布局 class 类型共享一个共同的初始序列,如果第一个但许多成员具有布局兼容的类型;
- 对于包含共享公共初始序列的 class 类型成员的联合类型,允许使用任一联合成员 (9.2p18) 检查此类初始序列的成员。
– 对于
reinterpret_cast
ed 指向结构的指针共享一个公共初始序列,没有类似的明确保证。 – 但是,可以保证指向结构的指针指向其初始成员 (9.2p19)。
仅使用这些信息,我发现无法推断 Element
和 KeyValuePair
结构是否共享一个共同的初始序列,或者是否有任何其他共同点可以证明我的 reinterpret_cast
.
顺便说一句,如果您认为为此目的使用 reinterpret_cast
是不合适的,而且我确实面临 XY 问题,因此我应该简单地做一些其他事情来实现我的目标,请告诉我。
My question is: is this use of reinterpret_cast well-defined, or does it invoke undefined behavior?
reinterpret_cast
在这里是错误的方法,你只是违反了严格的别名。 reinterpret_cast
和union
在这里出现分歧有点令人费解,但是这种情况的措辞非常清楚。
这样简单地定义联合可能会更好:
union elem_t {
Element e{}; KeyValuePair p;
/* special member functions defined if necessary */
};
... 并将其用作矢量元素类型。请注意,在确定布局兼容性时忽略 cv 限定 - [basic.types]/11:
Two types cv1
T1
and cv2T2
are layout-compatible types ifT1
andT2
are the same type, […]
因此 Element
和 KeyValuePair
确实共享一个共同的初始序列,并且访问 p
的相应成员,前提是 e
是活动的,是明确定义的.
另一种方法:定义
struct KeyValuePair {
Key key;
mutable Value value;
};
struct Element : KeyValuePair {
int flags;
};
现在提供一个迭代器,它简单地包装向量中的 const_iterator
并向上转换要公开的 references/pointers。 key
不可修改,但 value
可以。