I have looked at the following — related — questions, and none of them seem to address my exact issue: one, two, three.
I am writing a collection of which the elements (key-value pairs) are stored along with some bookkeeping information:
struct Element {
Key key;
Value value;
int flags;
};
std::vector<Element> elements;
(For simplicity, suppose that both Key
and Value
are standard-layout types. The collection won't be used with any other types anyway.)
In order to support iterator-based access, I've written iterators that override operator->
and operator*
to return to the user a pointer and a reference, respectively, to the key-value pair. However, due to the nature of the collection, the user is never allowed to change the returned key. For this reason, I've declared a KeyValuePair
structure:
struct KeyValuePair {
const Key key;
Value value;
};
And I've implemented operator->
on the iterator like this:
struct iterator {
size_t index;
KeyValuePair *operator->() {
return reinterpret_cast<KeyValuePair *>(&elements[index]);
}
};
My question is: is this use of reinterpret_cast
well-defined, or does it invoke undefined behavior? I have tried to interpret relevant parts of the standard and examined answers to questions about similar issues, however, I failed to draw a definitive conclusion from them, because…:
key
and value
) that only differ in const
-qualification;T
and cv T
are layout-compatible, but it doesn't state the converse either; furthermore, it mandates that they should have the same representation and alignment requirements;reinterpret_cast
ed pointers-to-structs sharing a common initial sequence.
– it is, however, guaranteed that a pointer-to-struct points to its initial member (9.2p19).Using merely this information, I found it impossible to deduce whether the Element
and KeyValuePair
structs share a common initial sequence, or have anything other in common that would justify my reinterpret_cast
.
As an aside, if you think using reinterpret_cast
for this purpose is inappropriate, and I'm really facing an XY problem and therefore I should simply do something else to achieve my goal, let me know.
The result of a reinterpret_cast cannot safely be used for anything other than being cast back to its original type. Other uses are, at best, nonportable. The reinterpret_cast operator cannot cast away the const , volatile , or __unaligned attributes.
reinterpret_cast is a very special and dangerous type of casting operator. And is suggested to use it using proper data type i.e., (pointer data type should be same as original data type). It can typecast any pointer to any other data type. It is used when we want to work with bits.
reinterpret_cast s are applicable in two scenarios: convert integer types to pointer types and vice versa. convert one pointer type to another.
None of the casts happen at compile time. You only have the data at runtime, in general.
My question is: is this use of reinterpret_cast well-defined, or does it invoke undefined behavior?
reinterpret_cast
is the wrong approach here, you're simply violating strict aliasing. It is somewhat perplexing that reinterpret_cast
and union
diverge here, but the wording is very clear about this scenario.
You might be better off simply defining a union thusly:
union elem_t {
Element e{}; KeyValuePair p;
/* special member functions defined if necessary */
};
… and using that as your vector element type. Note that cv-qualification is ignored when determining layout-compability - [basic.types]/11:
Two types cv1
T1
and cv2T2
are layout-compatible types ifT1
andT2
are the same type, […]
Hence Element
and KeyValuePair
do indeed share a common initial sequence, and accessing the corresponding members of p
, provided e
is alive, is well-defined.
Another approach: Define
struct KeyValuePair {
Key key;
mutable Value value;
};
struct Element : KeyValuePair {
int flags;
};
Now provide an iterator that simply wraps a const_iterator
from the vector and upcasts the references/pointers to be exposed. key
won't be modifiable, but value
will be.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With